mirror of
https://github.com/microsoft/semantic-kernel.git
synced 2026-10-12 02:19:06 +00:00
Python: Updated plugins (#5827)
### 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.
-->
The loading of plugins and functions into Kernel was not fully
consistent and clear, this is now simplified.
Adding a plugin can now be done in two ways:
- multiple: `kernel.add_plugins[plugin1, plugin2]`
- single plugin: `kernel.add_plugin(plugin)`
- if the plugin here is a class, with methods that have the
kernel_function decorator then that is parsed into a plugin
Adding a function can now be done in three ways:
- multiple: `kernel.add_functions([func1, func2]` or
`kernel.add_function({'func1': func1})`
- single: `kernel.add_function('plugin_name', func1)`
- from a prompt: `kernel.add_function(function_name='func1',
prompt='test prompt', ...)`
In other words, all the different methods that were available have been
simplified, these methods also return the created plugin (or updated
plugin for the `add_functions` method. The `add_function` (singular)
method has a parameter `return_plugin` to control whether you get the
created or updated plugin, instead of the created function.
**An important callout:**
One big internal change that this introduces is that a function that
gets added to a plugin (new or existing) is automatically copied, the
metadata is deep-copied and the plugin_name in the metadata is updated,
so this sequence is valid now:
```python
@kernel_function(name='test')
def f(...):
etc
func = KernelFunctionFromMethod(method=f)
func2 = kernel.add_function('plugin', func)
assert func != func2
```
Also closes: #5855
### Description
<!-- Describe your changes, the overall approach, the underlying design.
These notes will help understanding how your code works. Thanks! -->
Removed KernelPluginCollection
Updated KernelPlugin
added from_... method to load from different sources
Updated KernelFunctionFromPrompt
added from_yaml and from_directory methods.
Added and updated tests to 99% coverage of KernelPlugin and
KernelFunctionFromPrompt
### 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 😄
This commit is contained in:
@@ -23,7 +23,7 @@ repos:
|
||||
- id: black
|
||||
files: \.py$
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.3.4
|
||||
rev: v0.3.5
|
||||
hooks:
|
||||
- id: ruff
|
||||
args: [ --fix, --exit-non-zero-on-fix ]
|
||||
|
||||
+2
-2
@@ -87,7 +87,7 @@ prompt_template_config = sk.PromptTemplateConfig(
|
||||
execution_settings=req_settings,
|
||||
)
|
||||
|
||||
function = kernel.create_function_from_prompt(
|
||||
function = kernel.add_function(
|
||||
function_name="tldr_function",
|
||||
plugin_name="tldr_plugin",
|
||||
prompt_template_config=prompt_template_config,
|
||||
@@ -107,7 +107,7 @@ if __name__ == "__main__":
|
||||
|
||||
```python
|
||||
# Create a reusable function summarize function
|
||||
summarize = kernel.create_function_from_prompt(
|
||||
summarize = kernel.add_function(
|
||||
function_name="tldr_function",
|
||||
plugin_name="tldr_plugin",
|
||||
prompt="{{$input}}\n\nOne line TLDR with the fewest words.",
|
||||
|
||||
@@ -1,344 +1,340 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "3c93ac5b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Running Prompt Functions Inline"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "40201641",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The [previous notebook](./02-running-prompts-from-file.ipynb)\n",
|
||||
"showed how to define a semantic function using a prompt template stored on a file.\n",
|
||||
"\n",
|
||||
"In this notebook, we'll show how to use the Semantic Kernel to define functions inline with your python code. This can be useful in a few scenarios:\n",
|
||||
"\n",
|
||||
"* Dynamically generating the prompt using complex rules at runtime\n",
|
||||
"* Writing prompts by editing Python code instead of TXT files.\n",
|
||||
"* Easily creating demos, like this document\n",
|
||||
"\n",
|
||||
"Prompt templates are defined using the SK template language, which allows to reference variables and functions. Read [this doc](https://aka.ms/sk/howto/configurefunction) to learn more about the design decisions for prompt templating. \n",
|
||||
"\n",
|
||||
"For now we'll use only the `{{$input}}` variable, and see more complex templates later.\n",
|
||||
"\n",
|
||||
"Almost all semantic function prompts have a reference to `{{$input}}`, which is the default way\n",
|
||||
"a user can import content from the context variables."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "d90b0c13",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Prepare a semantic kernel instance first, loading also the AI service settings defined in the [Setup notebook](00-getting-started.ipynb):"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "1da651d4",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!python -m pip install semantic-kernel==0.9.5b1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"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,
|
||||
"id": "3712b7c3",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import semantic_kernel as sk\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",
|
||||
"service_id = None\n",
|
||||
"if selectedService == Service.OpenAI:\n",
|
||||
" from semantic_kernel.connectors.ai.open_ai import OpenAITextCompletion\n",
|
||||
"\n",
|
||||
" api_key, org_id = sk.openai_settings_from_dot_env()\n",
|
||||
" 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",
|
||||
" )"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "589733c5",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Let's use a prompt to create a semantic function used to summarize content, allowing for some creativity and a sufficient number of tokens.\n",
|
||||
"\n",
|
||||
"The function will take in input the text to summarize."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "ae29c207",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"prompt = \"\"\"{{$input}}\n",
|
||||
"Summarize the content above.\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"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(\n",
|
||||
" function_name=\"summarizeFunc\",\n",
|
||||
" plugin_name=\"summarizePlugin\",\n",
|
||||
" prompt_template_config=prompt_template_config,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "f26b90c4",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Set up some content to summarize, here's an extract about Demo, an ancient Greek poet, taken from Wikipedia (https://en.wikipedia.org/wiki/Demo_(ancient_Greek_poet))."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "314557fb",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"input_text = \"\"\"\n",
|
||||
"Demo (ancient Greek poet)\n",
|
||||
"From Wikipedia, the free encyclopedia\n",
|
||||
"Demo or Damo (Greek: Δεμώ, Δαμώ; fl. c. AD 200) was a Greek woman of the Roman period, known for a single epigram, engraved upon the Colossus of Memnon, which bears her name. She speaks of herself therein as a lyric poetess dedicated to the Muses, but nothing is known of her life.[1]\n",
|
||||
"Identity\n",
|
||||
"Demo was evidently Greek, as her name, a traditional epithet of Demeter, signifies. The name was relatively common in the Hellenistic world, in Egypt and elsewhere, and she cannot be further identified. The date of her visit to the Colossus of Memnon cannot be established with certainty, but internal evidence on the left leg suggests her poem was inscribed there at some point in or after AD 196.[2]\n",
|
||||
"Epigram\n",
|
||||
"There are a number of graffiti inscriptions on the Colossus of Memnon. Following three epigrams by Julia Balbilla, a fourth epigram, in elegiac couplets, entitled and presumably authored by \"Demo\" or \"Damo\" (the Greek inscription is difficult to read), is a dedication to the Muses.[2] The poem is traditionally published with the works of Balbilla, though the internal evidence suggests a different author.[1]\n",
|
||||
"In the poem, Demo explains that Memnon has shown her special respect. In return, Demo offers the gift for poetry, as a gift to the hero. At the end of this epigram, she addresses Memnon, highlighting his divine status by recalling his strength and holiness.[2]\n",
|
||||
"Demo, like Julia Balbilla, writes in the artificial and poetic Aeolic dialect. The language indicates she was knowledgeable in Homeric poetry—'bearing a pleasant gift', for example, alludes to the use of that phrase throughout the Iliad and Odyssey.[a][2] \n",
|
||||
"\"\"\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "bf0f2330",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"...and run the summary function:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "7b0e3b0c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"summary = await kernel.invoke(summarize, sk.KernelArguments(input=input_text))\n",
|
||||
"\n",
|
||||
"print(summary)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "1c2c1262",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Using ChatCompletion for Semantic Plugins"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"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_service` and `AzureChatCompletion` or `OpenAIChatCompletion`"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "4777f447",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Here's one more example of how to write an inline Semantic Function that gives a TLDR for a piece of text using a ChatCompletion model\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "c5886aeb",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"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",
|
||||
" )"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "ea8128c8",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"prompt = \"\"\"\n",
|
||||
"{{$input}}\n",
|
||||
"\n",
|
||||
"Give me the TLDR in 5 words or less.\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"text = \"\"\"\n",
|
||||
" 1) A robot may not injure a human being or, through inaction,\n",
|
||||
" allow a human being to come to harm.\n",
|
||||
"\n",
|
||||
" 2) A robot must obey orders given it by human beings except where\n",
|
||||
" such orders would conflict with the First Law.\n",
|
||||
"\n",
|
||||
" 3) A robot must protect its own existence as long as such protection\n",
|
||||
" does not conflict with the First or Second Law.\n",
|
||||
"\"\"\"\n",
|
||||
"\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=\"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",
|
||||
"tldr_function = kernel.create_function_from_prompt(\n",
|
||||
" function_name=\"tldrFunction\",\n",
|
||||
" plugin_name=\"tldrPlugin\",\n",
|
||||
" prompt_template_config=prompt_template_config,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"summary = await kernel.invoke(tldr_function, sk.KernelArguments(input=text))\n",
|
||||
"\n",
|
||||
"print(f\"Output: {summary}\")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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
|
||||
"cells": [
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "3c93ac5b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Running Prompt Functions Inline\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "40201641",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The [previous notebook](./02-running-prompts-from-file.ipynb)\n",
|
||||
"showed how to define a semantic function using a prompt template stored on a file.\n",
|
||||
"\n",
|
||||
"In this notebook, we'll show how to use the Semantic Kernel to define functions inline with your python code. This can be useful in a few scenarios:\n",
|
||||
"\n",
|
||||
"- Dynamically generating the prompt using complex rules at runtime\n",
|
||||
"- Writing prompts by editing Python code instead of TXT files.\n",
|
||||
"- Easily creating demos, like this document\n",
|
||||
"\n",
|
||||
"Prompt templates are defined using the SK template language, which allows to reference variables and functions. Read [this doc](https://aka.ms/sk/howto/configurefunction) to learn more about the design decisions for prompt templating.\n",
|
||||
"\n",
|
||||
"For now we'll use only the `{{$input}}` variable, and see more complex templates later.\n",
|
||||
"\n",
|
||||
"Almost all semantic function prompts have a reference to `{{$input}}`, which is the default way\n",
|
||||
"a user can import content from the context variables.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "d90b0c13",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Prepare a semantic kernel instance first, loading also the AI service settings defined in the [Setup notebook](00-getting-started.ipynb):\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "1da651d4",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!python -m pip install semantic-kernel==0.9.5b1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"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,
|
||||
"id": "3712b7c3",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import semantic_kernel as sk\n",
|
||||
"\n",
|
||||
"kernel = sk.Kernel()\n",
|
||||
"\n",
|
||||
"service_id = None\n",
|
||||
"if selectedService == Service.OpenAI:\n",
|
||||
" from semantic_kernel.connectors.ai.open_ai import OpenAITextCompletion\n",
|
||||
"\n",
|
||||
" api_key, org_id = sk.openai_settings_from_dot_env()\n",
|
||||
" 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",
|
||||
" )"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "589733c5",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Let's use a prompt to create a semantic function used to summarize content, allowing for some creativity and a sufficient number of tokens.\n",
|
||||
"\n",
|
||||
"The function will take in input the text to summarize.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "ae29c207",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"prompt = \"\"\"{{$input}}\n",
|
||||
"Summarize the content above.\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"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.add_function(\n",
|
||||
" function_name=\"summarizeFunc\",\n",
|
||||
" plugin_name=\"summarizePlugin\",\n",
|
||||
" prompt_template_config=prompt_template_config,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "f26b90c4",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Set up some content to summarize, here's an extract about Demo, an ancient Greek poet, taken from Wikipedia (https://en.wikipedia.org/wiki/Demo_(ancient_Greek_poet)).\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "314557fb",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"input_text = \"\"\"\n",
|
||||
"Demo (ancient Greek poet)\n",
|
||||
"From Wikipedia, the free encyclopedia\n",
|
||||
"Demo or Damo (Greek: Δεμώ, Δαμώ; fl. c. AD 200) was a Greek woman of the Roman period, known for a single epigram, engraved upon the Colossus of Memnon, which bears her name. She speaks of herself therein as a lyric poetess dedicated to the Muses, but nothing is known of her life.[1]\n",
|
||||
"Identity\n",
|
||||
"Demo was evidently Greek, as her name, a traditional epithet of Demeter, signifies. The name was relatively common in the Hellenistic world, in Egypt and elsewhere, and she cannot be further identified. The date of her visit to the Colossus of Memnon cannot be established with certainty, but internal evidence on the left leg suggests her poem was inscribed there at some point in or after AD 196.[2]\n",
|
||||
"Epigram\n",
|
||||
"There are a number of graffiti inscriptions on the Colossus of Memnon. Following three epigrams by Julia Balbilla, a fourth epigram, in elegiac couplets, entitled and presumably authored by \"Demo\" or \"Damo\" (the Greek inscription is difficult to read), is a dedication to the Muses.[2] The poem is traditionally published with the works of Balbilla, though the internal evidence suggests a different author.[1]\n",
|
||||
"In the poem, Demo explains that Memnon has shown her special respect. In return, Demo offers the gift for poetry, as a gift to the hero. At the end of this epigram, she addresses Memnon, highlighting his divine status by recalling his strength and holiness.[2]\n",
|
||||
"Demo, like Julia Balbilla, writes in the artificial and poetic Aeolic dialect. The language indicates she was knowledgeable in Homeric poetry—'bearing a pleasant gift', for example, alludes to the use of that phrase throughout the Iliad and Odyssey.[a][2] \n",
|
||||
"\"\"\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "bf0f2330",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"...and run the summary function:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "7b0e3b0c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"summary = await kernel.invoke(summarize, sk.KernelArguments(input=input_text))\n",
|
||||
"\n",
|
||||
"print(summary)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "1c2c1262",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Using ChatCompletion for Semantic Plugins\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"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_service` and `AzureChatCompletion` or `OpenAIChatCompletion`\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "4777f447",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Here's one more example of how to write an inline Semantic Function that gives a TLDR for a piece of text using a ChatCompletion model\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "c5886aeb",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"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",
|
||||
" )"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "ea8128c8",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"prompt = \"\"\"\n",
|
||||
"{{$input}}\n",
|
||||
"\n",
|
||||
"Give me the TLDR in 5 words or less.\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"text = \"\"\"\n",
|
||||
" 1) A robot may not injure a human being or, through inaction,\n",
|
||||
" allow a human being to come to harm.\n",
|
||||
"\n",
|
||||
" 2) A robot must obey orders given it by human beings except where\n",
|
||||
" such orders would conflict with the First Law.\n",
|
||||
"\n",
|
||||
" 3) A robot must protect its own existence as long as such protection\n",
|
||||
" does not conflict with the First or Second Law.\n",
|
||||
"\"\"\"\n",
|
||||
"\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=\"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",
|
||||
"tldr_function = kernel.add_function(\n",
|
||||
" function_name=\"tldrFunction\",\n",
|
||||
" plugin_name=\"tldrPlugin\",\n",
|
||||
" prompt_template_config=prompt_template_config,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"summary = await kernel.invoke(tldr_function, sk.KernelArguments(input=text))\n",
|
||||
"\n",
|
||||
"print(f\"Output: {summary}\")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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
|
||||
}
|
||||
|
||||
@@ -8,15 +8,15 @@
|
||||
"source": [
|
||||
"# 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 the kernel arguments 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 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 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 chat history 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 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. "
|
||||
"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.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -50,10 +50,6 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import semantic_kernel as sk\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",
|
||||
@@ -82,7 +78,7 @@
|
||||
"id": "7971783d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Let's define a prompt outlining a dialogue chat bot."
|
||||
"Let's define a prompt outlining a dialogue chat bot.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -107,7 +103,7 @@
|
||||
"id": "61716b16",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Register your semantic function"
|
||||
"Register your semantic function\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -143,7 +139,7 @@
|
||||
" execution_settings=execution_settings,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"chat_function = kernel.create_function_from_prompt(\n",
|
||||
"chat_function = kernel.add_function(\n",
|
||||
" function_name=\"chat\",\n",
|
||||
" plugin_name=\"chatPlugin\",\n",
|
||||
" prompt_template_config=prompt_template_config,\n",
|
||||
@@ -167,7 +163,7 @@
|
||||
"id": "6e8a676f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Initialize the Kernel Arguments"
|
||||
"Initialize the Kernel Arguments\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -186,7 +182,7 @@
|
||||
"id": "4ce7c497",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Chat with the Bot"
|
||||
"Chat with the Bot\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -206,7 +202,7 @@
|
||||
"id": "a5b03748",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Update the history with the output"
|
||||
"Update the history with the output\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -225,7 +221,7 @@
|
||||
"id": "23a2eb02",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Keep Chatting!"
|
||||
"Keep Chatting!\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -295,7 +291,7 @@
|
||||
"id": "c30bac97",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"After chatting for a while, we have built a growing history, which we are attaching to each prompt and which contains the full conversation. Let's take a look!"
|
||||
"After chatting for a while, we have built a growing history, which we are attaching to each prompt and which contains the full conversation. Let's take a look!\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,208 +1,204 @@
|
||||
{
|
||||
"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.9.5b1\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\n",
|
||||
"from semantic_kernel.memory.semantic_text_memory import SemanticTextMemory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"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": null,
|
||||
"id": "8f8dcbc6",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"kernel = sk.Kernel()\n",
|
||||
"\n",
|
||||
"# Configure LLM service\n",
|
||||
"if selectedService == Service.HuggingFace:\n",
|
||||
" # Feel free to update this model to any other model available on Hugging Face\n",
|
||||
" text_service_id = \"HuggingFaceM4/tiny-random-LlamaForCausalLM\"\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",
|
||||
" memory = SemanticTextMemory(storage=sk.memory.VolatileMemoryStore(), embeddings_generator=embedding_svc)\n",
|
||||
" kernel.import_plugin_from_object(sk.core_plugins.TextMemoryPlugin(memory), \"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": [
|
||||
"collection_id = \"generic\"\n",
|
||||
"\n",
|
||||
"await memory.save_information(collection=collection_id, id=\"info1\", text=\"Sharks are fish.\")\n",
|
||||
"await memory.save_information(collection=collection_id, id=\"info2\", text=\"Whales are mammals.\")\n",
|
||||
"await memory.save_information(collection=collection_id, id=\"info3\", text=\"Penguins are birds.\")\n",
|
||||
"await memory.save_information(collection=collection_id, id=\"info4\", text=\"Dolphins are mammals.\")\n",
|
||||
"await memory.save_information(collection=collection_id, id=\"info5\", text=\"Flies are insects.\")\n",
|
||||
"\n",
|
||||
"# Define prompt function using SK prompt template language\n",
|
||||
"my_prompt = \"\"\"I know these animal facts: \n",
|
||||
"- {{recall 'fact about sharks'}}\n",
|
||||
"- {{recall 'fact about whales'}} \n",
|
||||
"- {{recall 'fact about penguins'}} \n",
|
||||
"- {{recall 'fact about dolphins'}} \n",
|
||||
"- {{recall 'fact about flies'}}\n",
|
||||
"Now, tell me something about: {{$request}}\"\"\"\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(\n",
|
||||
" function_name=\"text_complete\",\n",
|
||||
" plugin_name=\"TextCompletionPlugin\",\n",
|
||||
" prompt_template_config=prompt_template_config,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"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": [
|
||||
"output = await kernel.invoke(\n",
|
||||
" my_function,\n",
|
||||
" request=\"What are whales?\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"output = str(output).strip()\n",
|
||||
"\n",
|
||||
"query_result1 = await memory.search(\n",
|
||||
" collection=collection_id, query=\"What are sharks?\", limit=1, min_relevance_score=0.3\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(f\"The queried result for 'What are sharks?' is {query_result1[0].text}\")\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
|
||||
"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.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "a77bdf89",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!python -m pip install semantic-kernel==0.9.5b1\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": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"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.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "8f8dcbc6",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"kernel = sk.Kernel()\n",
|
||||
"\n",
|
||||
"# Configure LLM service\n",
|
||||
"if selectedService == Service.HuggingFace:\n",
|
||||
" # Feel free to update this model to any other model available on Hugging Face\n",
|
||||
" text_service_id = \"HuggingFaceM4/tiny-random-LlamaForCausalLM\"\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",
|
||||
" memory = SemanticTextMemory(storage=sk.memory.VolatileMemoryStore(), embeddings_generator=embedding_svc)\n",
|
||||
" kernel.add_plugin(sk.core_plugins.TextMemoryPlugin(memory), \"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.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "d096504c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"collection_id = \"generic\"\n",
|
||||
"\n",
|
||||
"await memory.save_information(collection=collection_id, id=\"info1\", text=\"Sharks are fish.\")\n",
|
||||
"await memory.save_information(collection=collection_id, id=\"info2\", text=\"Whales are mammals.\")\n",
|
||||
"await memory.save_information(collection=collection_id, id=\"info3\", text=\"Penguins are birds.\")\n",
|
||||
"await memory.save_information(collection=collection_id, id=\"info4\", text=\"Dolphins are mammals.\")\n",
|
||||
"await memory.save_information(collection=collection_id, id=\"info5\", text=\"Flies are insects.\")\n",
|
||||
"\n",
|
||||
"# Define prompt function using SK prompt template language\n",
|
||||
"my_prompt = \"\"\"I know these animal facts: \n",
|
||||
"- {{recall 'fact about sharks'}}\n",
|
||||
"- {{recall 'fact about whales'}} \n",
|
||||
"- {{recall 'fact about penguins'}} \n",
|
||||
"- {{recall 'fact about dolphins'}} \n",
|
||||
"- {{recall 'fact about flies'}}\n",
|
||||
"Now, tell me something about: {{$request}}\"\"\"\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.add_function(\n",
|
||||
" function_name=\"text_complete\",\n",
|
||||
" plugin_name=\"TextCompletionPlugin\",\n",
|
||||
" prompt_template_config=prompt_template_config,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"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.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "628c843e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"output = await kernel.invoke(\n",
|
||||
" my_function,\n",
|
||||
" request=\"What are whales?\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"output = str(output).strip()\n",
|
||||
"\n",
|
||||
"query_result1 = await memory.search(\n",
|
||||
" collection=collection_id, query=\"What are sharks?\", limit=1, min_relevance_score=0.3\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(f\"The queried result for 'What are sharks?' is {query_result1[0].text}\")\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
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+506
-519
File diff suppressed because it is too large
Load Diff
@@ -32,7 +32,7 @@ async def main():
|
||||
)
|
||||
|
||||
# Import the ConversationSummaryPlugin
|
||||
kernel.import_plugin_from_object(
|
||||
kernel.add_plugin(
|
||||
ConversationSummaryPlugin(kernel=kernel, prompt_template_config=prompt_template_config),
|
||||
plugin_name="ConversationSummaryPlugin",
|
||||
)
|
||||
@@ -58,7 +58,7 @@ async def main():
|
||||
)
|
||||
|
||||
# Create the function
|
||||
chat_function = kernel.create_function_from_prompt(
|
||||
chat_function = kernel.add_function(
|
||||
prompt=prompt,
|
||||
plugin_name="Summarize_Conversation",
|
||||
function_name="Chat",
|
||||
|
||||
@@ -31,7 +31,7 @@ async def main():
|
||||
)
|
||||
|
||||
# Import the ConversationSummaryPlugin
|
||||
kernel.import_plugin_from_object(
|
||||
kernel.add_plugin(
|
||||
ConversationSummaryPlugin(kernel=kernel, prompt_template_config=prompt_template_config),
|
||||
plugin_name="ConversationSummaryPlugin",
|
||||
)
|
||||
@@ -56,7 +56,7 @@ async def main():
|
||||
)
|
||||
|
||||
# Run the prompt
|
||||
chat_function = kernel.create_function_from_prompt(
|
||||
chat_function = kernel.add_function(
|
||||
prompt=prompt,
|
||||
plugin_name="Summarize_Conversation",
|
||||
function_name="Chat",
|
||||
|
||||
@@ -52,7 +52,7 @@ async def main():
|
||||
# use_chat: True to use chat completion, False to use text completion
|
||||
kernel = add_service(kernel=kernel, use_chat=True)
|
||||
|
||||
light_plugin = kernel.import_plugin_from_object(
|
||||
light_plugin = kernel.add_plugin(
|
||||
LightPlugin(),
|
||||
plugin_name="LightPlugin",
|
||||
)
|
||||
|
||||
@@ -14,7 +14,7 @@ class Math:
|
||||
Description: MathPlugin provides a set of functions to make Math calculations.
|
||||
|
||||
Usage:
|
||||
kernel.import_plugin_from_object(MathPlugin(), plugin_name="math")
|
||||
kernel.add_plugin(MathPlugin(), plugin_name="math")
|
||||
|
||||
Examples:
|
||||
{{math.Add}} => Returns the sum of input and amount (provided in the KernelArguments)
|
||||
|
||||
@@ -21,9 +21,7 @@ async def main():
|
||||
prompt = f"What is the intent of this request? {request}"
|
||||
print("0.0 Initial prompt")
|
||||
print("-------------------------")
|
||||
prompt_function = kernel.create_function_from_prompt(
|
||||
function_name="sample_zero", plugin_name="sample_plugin", prompt=prompt
|
||||
)
|
||||
prompt_function = kernel.add_function(function_name="sample_zero", plugin_name="sample_plugin", prompt=prompt)
|
||||
result = await kernel.invoke(prompt_function, request=request)
|
||||
print(result)
|
||||
print("-------------------------")
|
||||
@@ -33,9 +31,7 @@ async def main():
|
||||
You can choose between SendEmail, SendMessage, CompleteTask, CreateDocument."""
|
||||
print("1.0 Make the prompt more specific")
|
||||
print("-------------------------")
|
||||
prompt_function = kernel.create_function_from_prompt(
|
||||
function_name="sample_one", plugin_name="sample_plugin", prompt=prompt
|
||||
)
|
||||
prompt_function = kernel.add_function(function_name="sample_one", plugin_name="sample_plugin", prompt=prompt)
|
||||
result = await kernel.invoke(prompt_function, request=request)
|
||||
print(result)
|
||||
print("-------------------------")
|
||||
@@ -47,9 +43,7 @@ async def main():
|
||||
Intent: """
|
||||
print("2.0 Add structure to the output with formatting")
|
||||
print("-------------------------")
|
||||
prompt_function = kernel.create_function_from_prompt(
|
||||
function_name="sample_two", plugin_name="sample_plugin", prompt=prompt
|
||||
)
|
||||
prompt_function = kernel.add_function(function_name="sample_two", plugin_name="sample_plugin", prompt=prompt)
|
||||
result = await kernel.invoke(prompt_function, request=request)
|
||||
print(result)
|
||||
print("-------------------------")
|
||||
@@ -80,9 +74,7 @@ async def main():
|
||||
## Intent"""
|
||||
print("2.1 Add structure to the output with formatting (using Markdown and JSON)")
|
||||
print("-------------------------")
|
||||
prompt_function = kernel.create_function_from_prompt(
|
||||
function_name="sample_two_one", plugin_name="sample_plugin", prompt=prompt
|
||||
)
|
||||
prompt_function = kernel.add_function(function_name="sample_two_one", plugin_name="sample_plugin", prompt=prompt)
|
||||
result = await kernel.invoke(prompt_function, request=request)
|
||||
print(result)
|
||||
print("-------------------------")
|
||||
@@ -101,9 +93,7 @@ async def main():
|
||||
Intent: """
|
||||
print("3.0 Provide examples with few-shot prompting")
|
||||
print("-------------------------")
|
||||
prompt_function = kernel.create_function_from_prompt(
|
||||
function_name="sample_three", plugin_name="sample_plugin", prompt=prompt
|
||||
)
|
||||
prompt_function = kernel.add_function(function_name="sample_three", plugin_name="sample_plugin", prompt=prompt)
|
||||
result = await kernel.invoke(prompt_function, request=request)
|
||||
print(result)
|
||||
print("-------------------------")
|
||||
@@ -123,9 +113,7 @@ async def main():
|
||||
Intent: """
|
||||
print("4.0 Tell the AI what to do to avoid doing something wrong")
|
||||
print("-------------------------")
|
||||
prompt_function = kernel.create_function_from_prompt(
|
||||
function_name="sample_four", plugin_name="sample_plugin", prompt=prompt
|
||||
)
|
||||
prompt_function = kernel.add_function(function_name="sample_four", plugin_name="sample_plugin", prompt=prompt)
|
||||
result = await kernel.invoke(prompt_function, request=request)
|
||||
print(result)
|
||||
print("-------------------------")
|
||||
@@ -150,9 +138,7 @@ async def main():
|
||||
Intent: """
|
||||
print("5.0 Provide context to the AI")
|
||||
print("-------------------------")
|
||||
prompt_function = kernel.create_function_from_prompt(
|
||||
function_name="sample_five", plugin_name="sample_plugin", prompt=prompt
|
||||
)
|
||||
prompt_function = kernel.add_function(function_name="sample_five", plugin_name="sample_plugin", prompt=prompt)
|
||||
result = await kernel.invoke(prompt_function, request=request, history=history)
|
||||
print(result)
|
||||
print("-------------------------")
|
||||
|
||||
@@ -45,7 +45,7 @@ async def main():
|
||||
)
|
||||
|
||||
# Import the ConversationSummaryPlugin
|
||||
kernel.import_plugin_from_object(
|
||||
kernel.add_plugin(
|
||||
ConversationSummaryPlugin(kernel=kernel, prompt_template_config=prompt_template_config),
|
||||
plugin_name="ConversationSummaryPlugin",
|
||||
)
|
||||
|
||||
@@ -40,7 +40,7 @@ async def main():
|
||||
)
|
||||
|
||||
# Run the prompt
|
||||
chat_function = kernel.create_function_from_prompt(
|
||||
chat_function = kernel.add_function(
|
||||
prompt=prompt,
|
||||
plugin_name="Summarize_Conversation",
|
||||
function_name="Chat",
|
||||
|
||||
@@ -18,7 +18,7 @@ async def main():
|
||||
kernel = add_service(kernel=kernel, use_chat=True)
|
||||
|
||||
# Import the TimePlugin
|
||||
time = kernel.import_plugin_from_object(TimePlugin(), "TimePlugin")
|
||||
time = kernel.add_plugin(TimePlugin(), "TimePlugin")
|
||||
|
||||
# Import the WriterPlugin from the plugins directory.
|
||||
script_directory = os.path.dirname(__file__)
|
||||
|
||||
@@ -18,9 +18,9 @@ async def main():
|
||||
OpenAIChatCompletion(service_id=service_id, ai_model_id="gpt-3.5-turbo", api_key=api_key, org_id=org_id)
|
||||
)
|
||||
|
||||
kernel.import_plugin_from_object(MathPlugin(), "math")
|
||||
kernel.import_plugin_from_object(TimePlugin(), "time")
|
||||
kernel.import_plugin_from_object(TextPlugin(), "text")
|
||||
kernel.add_plugin(MathPlugin(), "math")
|
||||
kernel.add_plugin(TimePlugin(), "time")
|
||||
kernel.add_plugin(TextPlugin(), "text")
|
||||
|
||||
# create an instance of action planner.
|
||||
planner = ActionPlanner(kernel, service_id)
|
||||
|
||||
@@ -48,7 +48,7 @@ req_settings.auto_invoke_kernel_functions = True
|
||||
## 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
|
||||
|
||||
|
||||
chat_function = kernel.create_function_from_prompt(
|
||||
chat_function = kernel.add_function(
|
||||
prompt=system_message + """{{$chat_history}}{{$user_input}}""",
|
||||
function_name="chat",
|
||||
plugin_name="chat",
|
||||
|
||||
@@ -35,7 +35,7 @@ req_settings.top_p = 0.8
|
||||
req_settings.auto_invoke_kernel_functions = False
|
||||
|
||||
|
||||
chat_function = kernel.create_function_from_prompt(
|
||||
chat_function = kernel.add_function(
|
||||
prompt="""{{system_message}}{{#each chat_history}}{{#message role=role}}{{~content~}}{{/message}} {{/each}}""",
|
||||
function_name="chat",
|
||||
plugin_name="chat",
|
||||
|
||||
@@ -35,7 +35,7 @@ req_settings.top_p = 0.8
|
||||
req_settings.auto_invoke_kernel_functions = False
|
||||
|
||||
|
||||
chat_function = kernel.create_function_from_prompt(
|
||||
chat_function = kernel.add_function(
|
||||
prompt="""{{system_message}}{% for item in chat_history %}{{ message(item) }}{% endfor %}""",
|
||||
function_name="chat",
|
||||
plugin_name="chat",
|
||||
|
||||
@@ -69,7 +69,7 @@ prompt_template_config = PromptTemplateConfig(
|
||||
],
|
||||
execution_settings={"default": req_settings},
|
||||
)
|
||||
chat_function = kernel.create_function_from_prompt(
|
||||
chat_function = kernel.add_function(
|
||||
plugin_name="ChatBot", function_name="Chat", prompt_template_config=prompt_template_config
|
||||
)
|
||||
|
||||
|
||||
+3
-5
@@ -11,9 +11,7 @@ from semantic_kernel.connectors.ai.open_ai.prompt_execution_settings.azure_chat_
|
||||
AzureChatPromptExecutionSettings,
|
||||
ExtraBody,
|
||||
)
|
||||
from semantic_kernel.connectors.ai.open_ai.utils import (
|
||||
get_tool_call_object,
|
||||
)
|
||||
from semantic_kernel.connectors.ai.open_ai.utils import 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
|
||||
@@ -59,7 +57,7 @@ plugins_directory = os.path.join(__file__, "../../../../samples/plugins")
|
||||
# the joke plugin in the FunPlugins is a semantic plugin and has the function calling disabled.
|
||||
kernel.import_plugin_from_prompt_directory(plugins_directory, "FunPlugin")
|
||||
# the math plugin is a core plugin and has the function calling enabled.
|
||||
kernel.import_plugin_from_object(TimePlugin(), plugin_name="time")
|
||||
kernel.add_plugin(TimePlugin(), plugin_name="time")
|
||||
|
||||
# enabling or disabling function calling is done by setting the tool_choice parameter for the completion.
|
||||
# when the tool_choice parameter is set to "auto" the model will decide which function to use, if any.
|
||||
@@ -81,7 +79,7 @@ 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.")
|
||||
|
||||
chat_function = kernel.create_function_from_prompt(
|
||||
chat_function = kernel.add_function(
|
||||
plugin_name="ChatBot", function_name="Chat", prompt_template_config=prompt_template_config
|
||||
)
|
||||
|
||||
|
||||
@@ -5,9 +5,7 @@ import logging
|
||||
|
||||
import semantic_kernel as sk
|
||||
import semantic_kernel.connectors.ai.open_ai as sk_oai
|
||||
from semantic_kernel.connectors.ai.open_ai.contents.azure_chat_message_content import (
|
||||
AzureChatMessageContent,
|
||||
)
|
||||
from semantic_kernel.connectors.ai.open_ai.contents.azure_chat_message_content import AzureChatMessageContent
|
||||
from semantic_kernel.connectors.ai.open_ai.contents.function_call import FunctionCall
|
||||
from semantic_kernel.connectors.ai.open_ai.contents.tool_calls import ToolCall
|
||||
from semantic_kernel.connectors.ai.open_ai.prompt_execution_settings.azure_chat_prompt_execution_settings import (
|
||||
@@ -82,7 +80,7 @@ chat_history = ChatHistory()
|
||||
chat_history.add_user_message("Hi there, who are you?")
|
||||
chat_history.add_assistant_message("I am an AI assistant here to answer your questions.")
|
||||
|
||||
chat_function = kernel.create_function_from_prompt(
|
||||
chat_function = kernel.add_function(
|
||||
plugin_name="ChatBot", function_name="Chat", prompt_template_config=prompt_template_config
|
||||
)
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ async def main() -> None:
|
||||
)
|
||||
|
||||
memory = SemanticTextMemory(storage=acs_connector, embeddings_generator=embedding_gen)
|
||||
kernel.import_plugin_from_object(sk.core_plugins.TextMemoryPlugin(memory), "TextMemoryPlugin")
|
||||
kernel.add_plugin(sk.core_plugins.TextMemoryPlugin(memory), "TextMemoryPlugin")
|
||||
|
||||
print("Populating memory...")
|
||||
await populate_memory(kernel)
|
||||
|
||||
+2
-2
@@ -31,8 +31,8 @@ async def main():
|
||||
cur_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "resources")
|
||||
kernel.import_native_plugin_from_directory(cur_dir, "email_plugin")
|
||||
|
||||
kernel.import_plugin_from_object(MathPlugin(), "MathPlugin")
|
||||
kernel.import_plugin_from_object(TimePlugin(), "TimePlugin")
|
||||
kernel.add_plugin(MathPlugin(), "MathPlugin")
|
||||
kernel.add_plugin(TimePlugin(), "TimePlugin")
|
||||
|
||||
questions = [
|
||||
"What is the current hour number, plus 5?",
|
||||
|
||||
@@ -63,7 +63,7 @@ async def example2(kernel: sk.Kernel, service_id: str):
|
||||
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(
|
||||
oracle = kernel.add_function(
|
||||
function_name="oracle",
|
||||
plugin_name="OraclePlugin",
|
||||
template=prompt,
|
||||
@@ -111,7 +111,7 @@ async def main():
|
||||
|
||||
bing_connector = BingConnector(api_key=bing_api_key)
|
||||
bing = WebSearchEnginePlugin(bing_connector)
|
||||
kernel.import_plugin_from_object(bing, "bing")
|
||||
kernel.add_plugin(bing, "bing")
|
||||
|
||||
await example1(kernel, "bing")
|
||||
await example2(kernel, service_id)
|
||||
|
||||
@@ -26,7 +26,7 @@ async def main():
|
||||
),
|
||||
)
|
||||
connector = BingConnector(api_key=os.getenv("BING_API_KEY"))
|
||||
web_plugin = kernel.import_plugin_from_object(WebSearchEnginePlugin(connector), "WebSearch")
|
||||
web_plugin = kernel.add_plugin(WebSearchEnginePlugin(connector), "WebSearch")
|
||||
|
||||
print("---------------- Question 1 -----------------\n")
|
||||
|
||||
@@ -46,7 +46,7 @@ async def main():
|
||||
Answer:
|
||||
"""
|
||||
|
||||
req_settings = kernel.get_service("chat-gpt").get_prompt_execution_settings_class()(service_id=service_id)
|
||||
req_settings = kernel.get_prompt_execution_settings_from_service_id(service_id=service_id)
|
||||
req_settings.temperature = 0.2
|
||||
|
||||
prompt_template_config = sk.PromptTemplateConfig(
|
||||
@@ -57,7 +57,7 @@ async def main():
|
||||
)
|
||||
|
||||
question = "What is Semantic Kernel?"
|
||||
qna = kernel.create_function_from_prompt(
|
||||
qna = kernel.add_function(
|
||||
function_name="qna",
|
||||
plugin_name="WebSearch",
|
||||
prompt_template_config=prompt_template_config,
|
||||
|
||||
@@ -56,7 +56,7 @@ 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(
|
||||
chat_function = kernel.add_function(
|
||||
plugin_name="ChatBot", function_name="Chat", prompt_template_config=prompt_template_config
|
||||
)
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ settings.max_tokens = 2000
|
||||
settings.temperature = 0.7
|
||||
settings.top_p = 0.8
|
||||
|
||||
chat_function = kernel.create_function_from_prompt(
|
||||
chat_function = kernel.add_function(
|
||||
plugin_name="ChatBot",
|
||||
function_name="Chat",
|
||||
prompt="{{$chat_history}}{{$user_input}}",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import asyncio
|
||||
import os
|
||||
from functools import reduce
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Union
|
||||
from typing import TYPE_CHECKING, Any, Dict, List
|
||||
|
||||
import semantic_kernel as sk
|
||||
import semantic_kernel.connectors.ai.open_ai as sk_oai
|
||||
@@ -12,7 +12,7 @@ from semantic_kernel.connectors.ai.open_ai.contents.open_ai_streaming_chat_messa
|
||||
OpenAIStreamingChatMessageContent,
|
||||
)
|
||||
from semantic_kernel.connectors.ai.open_ai.prompt_execution_settings.open_ai_prompt_execution_settings import (
|
||||
OpenAIPromptExecutionSettings,
|
||||
OpenAIChatPromptExecutionSettings,
|
||||
)
|
||||
from semantic_kernel.connectors.ai.open_ai.utils import get_tool_call_object
|
||||
from semantic_kernel.contents.chat_history import ChatHistory
|
||||
@@ -52,10 +52,10 @@ plugins_directory = os.path.join(__file__, "../../../../samples/plugins")
|
||||
# the joke plugin in the FunPlugins is a semantic plugin and has the function calling disabled.
|
||||
# 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_from_object(MathPlugin(), plugin_name="math")
|
||||
kernel.import_plugin_from_object(TimePlugin(), plugin_name="time")
|
||||
kernel.add_plugin(MathPlugin(), plugin_name="math")
|
||||
kernel.add_plugin(TimePlugin(), plugin_name="time")
|
||||
|
||||
chat_function = kernel.create_function_from_prompt(
|
||||
chat_function = kernel.add_function(
|
||||
prompt="{{$chat_history}}{{$user_input}}",
|
||||
plugin_name="ChatBot",
|
||||
function_name="Chat",
|
||||
@@ -89,10 +89,10 @@ history.add_assistant_message("I am Mosscap, a chat bot. I'm trying to figure ou
|
||||
arguments = KernelArguments(settings=execution_settings)
|
||||
|
||||
|
||||
def print_tool_calls(message: Union[OpenAIChatMessageContent, OpenAIStreamingChatMessageContent]) -> None:
|
||||
def print_tool_calls(message: OpenAIChatMessageContent) -> None:
|
||||
# A helper method to pretty print the tool calls from the message.
|
||||
# This is only triggered if auto invoke tool calls is disabled.
|
||||
if isinstance(message, (OpenAIChatMessageContent, OpenAIStreamingChatMessageContent)):
|
||||
if isinstance(message, OpenAIChatMessageContent):
|
||||
tool_calls = message.tool_calls
|
||||
formatted_tool_calls = []
|
||||
for i, tool_call in enumerate(tool_calls, start=1):
|
||||
@@ -113,7 +113,7 @@ async def handle_streaming(
|
||||
chat_function: "KernelFunction",
|
||||
user_input: str,
|
||||
history: ChatHistory,
|
||||
execution_settings: OpenAIPromptExecutionSettings,
|
||||
execution_settings: OpenAIChatPromptExecutionSettings,
|
||||
) -> None:
|
||||
response = kernel.invoke_stream(
|
||||
chat_function,
|
||||
|
||||
@@ -46,7 +46,7 @@ async def main():
|
||||
),
|
||||
)
|
||||
|
||||
chat = kernel.create_function_from_prompt(
|
||||
chat = kernel.add_function(
|
||||
function_name="chat",
|
||||
plugin_name="ChatBot",
|
||||
prompt_template_config=prompt_template_config,
|
||||
|
||||
@@ -51,7 +51,7 @@ async def setup_chat_with_memory(
|
||||
},
|
||||
)
|
||||
|
||||
chat_func = kernel.create_function_from_prompt(
|
||||
chat_func = kernel.add_function(
|
||||
function_name="chat_with_memory",
|
||||
plugin_name="TextMemoryPlugin",
|
||||
prompt_template_config=prompt_template_config,
|
||||
@@ -91,7 +91,7 @@ async def main() -> None:
|
||||
kernel.add_service(palm_chat_completion)
|
||||
|
||||
memory = SemanticTextMemory(storage=sk.memory.VolatileMemoryStore(), embeddings_generator=palm_text_embed)
|
||||
kernel.import_plugin_from_object(TextMemoryPlugin(memory), "TextMemoryPlugin")
|
||||
kernel.add_plugin(TextMemoryPlugin(memory), "TextMemoryPlugin")
|
||||
|
||||
print("Populating memory...")
|
||||
await populate_memory(memory)
|
||||
|
||||
@@ -50,7 +50,7 @@ prompt_template_config = sk.PromptTemplateConfig(
|
||||
execution_settings=req_settings,
|
||||
)
|
||||
|
||||
chat_func = kernel.create_function_from_prompt(
|
||||
chat_func = kernel.add_function(
|
||||
plugin_name="PiratePlugin", function_name="Chat", prompt_template_config=prompt_template_config
|
||||
)
|
||||
|
||||
|
||||
@@ -6,8 +6,10 @@ from dotenv import load_dotenv
|
||||
|
||||
import semantic_kernel as sk
|
||||
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
|
||||
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
|
||||
from semantic_kernel.connectors.search_engine import GoogleConnector
|
||||
from semantic_kernel.core_plugins import WebSearchEnginePlugin
|
||||
from semantic_kernel.functions.kernel_arguments import KernelArguments
|
||||
|
||||
load_dotenv()
|
||||
|
||||
@@ -36,15 +38,15 @@ async def main():
|
||||
)
|
||||
|
||||
# Import the WebSearchEnginePlugin and pass the Google Connector to it.
|
||||
web_plugin = kernel.import_plugin_from_object(WebSearchEnginePlugin(connector), "WebSearch")
|
||||
web_plugin = kernel.add_plugin(WebSearchEnginePlugin(connector), "WebSearch")
|
||||
|
||||
# The search query
|
||||
prompt = "Who is Leonardo DiCaprio's current girlfriend?"
|
||||
search = web_plugin["searchAsync"]
|
||||
prompt = "Who is Leonardo DiCaprio's current girlfriend?"
|
||||
|
||||
# By default, only one search result is provided
|
||||
result = await search.invoke(prompt)
|
||||
print(result)
|
||||
result = await search.invoke(kernel, query=prompt)
|
||||
print(str(result))
|
||||
|
||||
"""
|
||||
Output:
|
||||
@@ -61,19 +63,22 @@ async def main():
|
||||
Answer:
|
||||
"""
|
||||
|
||||
qna = kernel.create_semantic_function(prompt, temperature=0.2)
|
||||
context = kernel.create_new_context()
|
||||
qna = kernel.add_function(
|
||||
plugin_name="qa",
|
||||
function_name="qna",
|
||||
prompt=prompt,
|
||||
prompt_execution_settings=PromptExecutionSettings(temperature=0.2),
|
||||
)
|
||||
|
||||
"""
|
||||
Two context parameters can be passed to the search engine plugin.
|
||||
- num_results controls the number of results returned by the web search.
|
||||
- offset controls the number of results to omit.
|
||||
"""
|
||||
context["num_results"] = "10"
|
||||
context["offset"] = "0"
|
||||
arguments = KernelArguments(num_results="10", offset="0")
|
||||
|
||||
result = await qna.invoke(context=context)
|
||||
print(result)
|
||||
result = await qna.invoke(kernel, arguments)
|
||||
print(str(result))
|
||||
|
||||
"""
|
||||
Output:
|
||||
|
||||
@@ -3,10 +3,9 @@ import logging
|
||||
|
||||
import semantic_kernel as sk
|
||||
from samples.utils import Colors
|
||||
from semantic_kernel.connectors.ai.open_ai import (
|
||||
AzureChatCompletion,
|
||||
OpenAIChatCompletion,
|
||||
)
|
||||
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion, OpenAIChatCompletion
|
||||
from semantic_kernel.functions.kernel_arguments import KernelArguments
|
||||
from semantic_kernel.kernel import Kernel
|
||||
|
||||
|
||||
def get_grounding_text():
|
||||
@@ -48,17 +47,17 @@ interment of his friend he conducted her to Geneva and placed her under the prot
|
||||
after this event Caroline became his wife."""
|
||||
|
||||
|
||||
def setup(use_azure: bool = False):
|
||||
logger = logging.getLogger()
|
||||
logger.setLevel(logging.DEBUG)
|
||||
kernel = sk.Kernel(log=logger)
|
||||
logger = logging.getLogger()
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
useAzureOpenAI = use_azure
|
||||
|
||||
def setup(use_azure: bool = False, plugin_name: str = "GroundingPlugin"):
|
||||
kernel = Kernel()
|
||||
|
||||
# Configure AI service used by the kernel
|
||||
if useAzureOpenAI:
|
||||
if use_azure:
|
||||
deployment, api_key, endpoint = sk.azure_openai_settings_from_dot_env()
|
||||
service_id = ("chat_completion",)
|
||||
service_id = "chat_completion"
|
||||
kernel.add_service(
|
||||
AzureChatCompletion(
|
||||
service_id=service_id,
|
||||
@@ -66,7 +65,6 @@ def setup(use_azure: bool = False):
|
||||
endpoint=endpoint,
|
||||
api_key=api_key,
|
||||
api_version="2023-12-01-preview",
|
||||
log=logger,
|
||||
),
|
||||
)
|
||||
else:
|
||||
@@ -79,11 +77,9 @@ def setup(use_azure: bool = False):
|
||||
# note: using plugins from the samples folder
|
||||
plugins_directory = "../samples/plugins/"
|
||||
|
||||
grounding_semantic_functions = kernel.import_plugin_from_prompt_directory(
|
||||
service_id, plugins_directory, "GroundingPlugin"
|
||||
)
|
||||
kernel.add_plugin(parent_directory=plugins_directory, plugin_name=plugin_name)
|
||||
|
||||
return kernel, grounding_semantic_functions
|
||||
return kernel
|
||||
|
||||
|
||||
def get_summary_text():
|
||||
@@ -93,37 +89,47 @@ def get_summary_text():
|
||||
return summary_text
|
||||
|
||||
|
||||
async def run_entity_extraction(kernel, semantic_functions, summary_text):
|
||||
context = kernel.create_new_context()
|
||||
context["topic"] = "people and places"
|
||||
context["example_entities"] = "John, Jane, mother, brother, Paris, Rome"
|
||||
async def run_entity_extraction(kernel: Kernel, plugin_name: str, summary_text: str):
|
||||
arguments = KernelArguments(
|
||||
topic="people and places", example_entities="John, Jane, mother, brother, Paris, Rome", input=summary_text
|
||||
)
|
||||
|
||||
extraction_result = semantic_functions["ExtractEntities"](summary_text, context=context)
|
||||
extraction_result = await kernel.invoke(
|
||||
plugin_name=plugin_name, function_name="ExtractEntities", arguments=arguments
|
||||
)
|
||||
|
||||
return extraction_result, context
|
||||
return extraction_result
|
||||
|
||||
|
||||
async def run_reference_check(semantic_functions, extraction_result, context):
|
||||
context["reference_context"] = get_grounding_text()
|
||||
|
||||
grounding_result = semantic_functions["ReferenceCheckEntities"](extraction_result.result, context=context)
|
||||
context["ungrounded_entities"] = grounding_result.result
|
||||
return grounding_result, context
|
||||
async def run_reference_check(kernel: Kernel, plugin_name: str, extraction_result):
|
||||
grounding_result = await kernel.invoke(
|
||||
plugin_name=plugin_name,
|
||||
function_name="ReferenceCheckEntities",
|
||||
input=str(extraction_result),
|
||||
reference_context=get_grounding_text(),
|
||||
)
|
||||
return grounding_result
|
||||
|
||||
|
||||
async def run_entity_excision(semantic_functions, summary_text, context):
|
||||
excision_result = semantic_functions["ExciseEntities"](summary_text, context=context)
|
||||
return excision_result, context
|
||||
async def run_entity_excision(kernel: Kernel, plugin_name: str, summary_text, grounding_result):
|
||||
excision_result = await kernel.invoke(
|
||||
plugin_name=plugin_name,
|
||||
function_name="ExciseEntities",
|
||||
input=summary_text,
|
||||
ungrounded_entities=grounding_result,
|
||||
)
|
||||
return excision_result
|
||||
|
||||
|
||||
async def run_grounding(use_azure: bool = False):
|
||||
kernel, semantic_functions = setup(use_azure)
|
||||
print(f"\n{Colors.CBOLD}Groundingsness Checking Plugins\n{Colors.CEND}")
|
||||
plugin_name = "GroundingPlugin"
|
||||
kernel = setup(use_azure, plugin_name=plugin_name)
|
||||
print(f"\n{Colors.CBOLD.value}Groundingsness Checking Plugins\n{Colors.CEND.value}")
|
||||
print(f"\n{ '-'*80 }\n")
|
||||
print(
|
||||
f"""{Colors.CGREEN}A well-known problem with large language models (LLMs) is that they make things up. These are sometimes called 'hallucinations' but a safer (and less anthropomorphic) term is 'ungrounded addition' - something in the text which cannot be firmly established. When attempting to establish whether or not something in an LLM response is 'true' we can either check for it in the supplied prompt (this is called 'narrow grounding') or use our general knowledge ('broad grounding'). Note that narrow grounding can lead to things being classified as 'true, but ungrounded.' For example "I live in Switzerland" is **not** _narrowly_ grounded in "I live in Geneva" even though it must be true (it **is** _broadly_ grounded). # noqa: E501
|
||||
f"""{Colors.CGREEN.value}A well-known problem with large language models (LLMs) is that they make things up. These are sometimes called 'hallucinations' but a safer (and less anthropomorphic) term is 'ungrounded addition' - something in the text which cannot be firmly established. When attempting to establish whether or not something in an LLM response is 'true' we can either check for it in the supplied prompt (this is called 'narrow grounding') or use our general knowledge ('broad grounding'). Note that narrow grounding can lead to things being classified as 'true, but ungrounded.' For example "I live in Switzerland" is **not** _narrowly_ grounded in "I live in Geneva" even though it must be true (it **is** _broadly_ grounded).
|
||||
|
||||
In this sample we run a simple grounding pipeline, to see if a summary text has any ungrounded additions as compared to the original, and use this information to improve the summary text. This can be done in three stages: # noqa: E501
|
||||
In this sample we run a simple grounding pipeline, to see if a summary text has any ungrounded additions as compared to the original, and use this information to improve the summary text. This can be done in three stages:
|
||||
|
||||
1. Make a list of the entities in the summary text
|
||||
1. Check to see if these entities appear in the original (grounding) text
|
||||
@@ -132,14 +138,14 @@ In this sample we run a simple grounding pipeline, to see if a summary text has
|
||||
What is an 'entity' in this context? In its simplest form, it's a named object such as a person or place (so 'Dean' or 'Seattle'). However, the idea could be a _claim_ which relates concepts (such as 'Dean lives near Seattle'). In this sample, we will keep to the simpler case of named objects.""" # noqa: E501
|
||||
)
|
||||
|
||||
print(f"\nThe grounding text: \n{Colors.CGREY}{get_grounding_text()}{Colors.CEND}")
|
||||
print(f"\nThe grounding text: \n{Colors.CGREY.value}{get_grounding_text()}{Colors.CEND.value}")
|
||||
|
||||
print(f"\n{ '-'*80 }\n")
|
||||
summary_text = get_summary_text()
|
||||
print(f"Summary text: \n{Colors.CBLUE}{summary_text}{Colors.CEND}")
|
||||
print(f"Summary text: \n{Colors.CBLUE.value}{summary_text}{Colors.CEND.value}")
|
||||
print(f"\n{ '-'*80 }\n")
|
||||
print(
|
||||
f"""{Colors.CGREEN}Some things to note:
|
||||
f"""{Colors.CGREEN.value}Some things to note:
|
||||
|
||||
- The implied residence of Geneva has been changed to Milan
|
||||
- Lucerne has been changed to Zurich
|
||||
@@ -153,28 +159,28 @@ The grounding plugin has three stages:
|
||||
2. Perform a reference check against the grounding text
|
||||
3. Excise any entities which failed the reference check from the summary
|
||||
|
||||
Now, let us start calling individual semantic functions.{Colors.CEND}"""
|
||||
Now, let us start calling individual semantic functions.{Colors.CEND.value}"""
|
||||
)
|
||||
print(f"\n{ '-'*80 }\n")
|
||||
print(
|
||||
f"{Colors.CGREEN}First we run the extraction function on the summary, this results in all the extracted entities.{Colors.CEND}" # noqa: E501
|
||||
f"{Colors.CGREEN.value}First we run the extraction function on the summary, this results in all the extracted entities.{Colors.CEND.value}" # noqa: E501
|
||||
)
|
||||
extraction_result, context = await run_entity_extraction(kernel, semantic_functions, summary_text)
|
||||
print(f"Extraction result: \n{Colors.CBLUE}{extraction_result.result}{Colors.CEND}")
|
||||
extraction_result = await run_entity_extraction(kernel, plugin_name, summary_text)
|
||||
print(f"Extraction result: \n{Colors.CBLUE.value}{str(extraction_result)}{Colors.CEND.value}")
|
||||
print(f"\n{ '-'*80 }\n")
|
||||
print(
|
||||
f"{Colors.CGREEN}Next we run the reference check function on the summary, this loads the grounding text as part of it in order to know the 'truth'. This returns a list of ungrounded entities.{Colors.CEND}" # noqa: E501
|
||||
f"{Colors.CGREEN.value}Next we run the reference check function on the summary, this loads the grounding text as part of it in order to know the 'truth'. This returns a list of ungrounded entities.{Colors.CEND.value}" # noqa: E501
|
||||
)
|
||||
grounding_result, context = await run_reference_check(semantic_functions, extraction_result, context)
|
||||
print(f"Grounding result: \n{Colors.CBLUE}{grounding_result.result}{Colors.CEND}")
|
||||
grounding_result = await run_reference_check(kernel, plugin_name, extraction_result)
|
||||
print(f"Grounding result: \n{Colors.CBLUE.value}{str(grounding_result)}{Colors.CEND.value}")
|
||||
print(f"\n{ '-'*80 }\n")
|
||||
print(
|
||||
f"{Colors.CGREEN}Finally we run the excision function on the summary, this removes the ungrounded entities from the summary.{Colors.CEND}" # noqa: E501
|
||||
f"{Colors.CGREEN.value}Finally we run the excision function on the summary, this removes the ungrounded entities from the summary.{Colors.CEND.value}" # noqa: E501
|
||||
)
|
||||
excision_result, context = await run_entity_excision(semantic_functions, summary_text, context)
|
||||
print(f"The final summary text: \n{Colors.CBLUE}{excision_result.result}{Colors.CEND}")
|
||||
excision_result = await run_entity_excision(kernel, plugin_name, summary_text, grounding_result)
|
||||
print(f"The final summary text: \n{Colors.CBLUE.value}{str(excision_result)}{Colors.CEND.value}")
|
||||
print(f"\n{ '-'*80 }\n")
|
||||
print(f"{Colors.CBOLD}Finished!{Colors.CEND}")
|
||||
print(f"{Colors.CBOLD.value}Finished!{Colors.CEND.value}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -6,9 +6,7 @@ import os
|
||||
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
|
||||
from semantic_kernel.contents.chat_history import ChatHistory
|
||||
from semantic_kernel.kernel import Kernel
|
||||
from semantic_kernel.utils.settings import (
|
||||
openai_settings_from_dot_env,
|
||||
)
|
||||
from semantic_kernel.utils.settings import openai_settings_from_dot_env
|
||||
|
||||
|
||||
async def main():
|
||||
@@ -27,7 +25,7 @@ async def main():
|
||||
chat_history = ChatHistory(system_message="Assistant is a large language model")
|
||||
|
||||
cur_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "resources")
|
||||
plugin = kernel.import_plugin_from_prompt_directory(cur_dir, "sample_plugins")
|
||||
plugin = kernel.add_plugin(plugin_name="sample_plugins", parent_directory=cur_dir)
|
||||
|
||||
result = await kernel.invoke(plugin["Parrot"], count=2, user_message="I love parrots.", chat_history=chat_history)
|
||||
print(result)
|
||||
|
||||
@@ -46,12 +46,10 @@ async def setup_chat_with_memory(
|
||||
|
||||
prompt_template_config = PromptTemplateConfig(
|
||||
template=prompt,
|
||||
execution_settings={
|
||||
service_id: kernel.get_service(service_id).get_prompt_execution_settings_class()(service_id=service_id)
|
||||
},
|
||||
execution_settings={service_id: kernel.get_prompt_execution_settings_from_service_id(service_id=service_id)},
|
||||
)
|
||||
|
||||
chat_func = kernel.create_function_from_prompt(
|
||||
chat_func = kernel.add_function(
|
||||
function_name="chat_with_memory",
|
||||
plugin_name="TextMemoryPlugin",
|
||||
prompt_template_config=prompt_template_config,
|
||||
@@ -94,7 +92,7 @@ async def main() -> None:
|
||||
kernel.add_service(embedding_gen)
|
||||
|
||||
memory = SemanticTextMemory(storage=sk.memory.VolatileMemoryStore(), embeddings_generator=embedding_gen)
|
||||
kernel.import_plugin_from_object(TextMemoryPlugin(memory), "TextMemoryPlugin")
|
||||
kernel.add_plugin(TextMemoryPlugin(memory), "TextMemoryPlugin")
|
||||
|
||||
print("Populating memory...")
|
||||
await populate_memory(memory)
|
||||
|
||||
@@ -29,10 +29,8 @@ async def main():
|
||||
)
|
||||
|
||||
cur_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "resources")
|
||||
kernel.import_native_plugin_from_directory(cur_dir, "email_plugin")
|
||||
|
||||
kernel.import_plugin_from_object(MathPlugin(), "MathPlugin")
|
||||
kernel.import_plugin_from_object(TimePlugin(), "TimePlugin")
|
||||
kernel.add_plugin(parent_directory=cur_dir, plugin_name="email_plugin")
|
||||
kernel.add_plugins({"MathPlugin": MathPlugin(), "TimePlugin": TimePlugin()})
|
||||
|
||||
questions = [
|
||||
"What is the current hour number, plus 5?",
|
||||
@@ -52,7 +50,7 @@ async def main():
|
||||
print(f"Q: {question}\nA: {result.final_answer}\n")
|
||||
|
||||
# Uncomment the following line to view the planner's process for completing the request
|
||||
# print(f"Chat history: {result.chat_history}\n")
|
||||
# print(f"\nChat history: {result.chat_history}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -8,6 +8,7 @@ import semantic_kernel.connectors.ai.open_ai as sk_oai
|
||||
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
|
||||
from semantic_kernel.contents.chat_history import ChatHistory
|
||||
from semantic_kernel.functions.kernel_arguments import KernelArguments
|
||||
from semantic_kernel.kernel import Kernel
|
||||
from semantic_kernel.prompt_template.input_variable import InputVariable
|
||||
from semantic_kernel.prompt_template.prompt_template_config import PromptTemplateConfig
|
||||
|
||||
@@ -31,7 +32,7 @@ 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):
|
||||
async def chat_request_example(kernel: Kernel, api_key, org_id):
|
||||
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
|
||||
@@ -70,7 +71,7 @@ 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_service(service_id).get_prompt_execution_settings_class()(service_id=service_id)
|
||||
settings = kernel.get_prompt_execution_settings_from_service_id(service_id=service_id)
|
||||
settings = _config_ban_tokens(settings, keys)
|
||||
|
||||
prompt_template_config = PromptTemplateConfig(
|
||||
@@ -90,7 +91,7 @@ async def chat_request_example(kernel, api_key, org_id):
|
||||
chat.add_user_message("Hi there, who are you?")
|
||||
chat.add_assistant_message("I am an AI assistant here to answer your questions.")
|
||||
|
||||
chat_function = kernel.create_function_from_prompt(
|
||||
chat_function = kernel.add_function(
|
||||
plugin_name="ChatBot", function_name="Chat", prompt_template_config=prompt_template_config
|
||||
)
|
||||
|
||||
@@ -111,7 +112,7 @@ async def chat_request_example(kernel, api_key, org_id):
|
||||
return chat, banned_words
|
||||
|
||||
|
||||
async def text_complete_request_example(kernel, api_key, org_id):
|
||||
async def text_complete_request_example(kernel: Kernel, api_key, org_id):
|
||||
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
|
||||
@@ -159,7 +160,7 @@ 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_service(service_id).get_prompt_execution_settings_class()(service_id=service_id)
|
||||
settings = kernel.get_prompt_execution_settings_from_service_id(service_id=service_id)
|
||||
settings = _config_ban_tokens(settings, keys)
|
||||
|
||||
prompt_template_config = PromptTemplateConfig(
|
||||
@@ -178,7 +179,7 @@ async def text_complete_request_example(kernel, api_key, org_id):
|
||||
|
||||
chat.add_user_message("The best pie flavor to have in autumn is")
|
||||
|
||||
text_function = kernel.create_function_from_prompt(
|
||||
text_function = kernel.add_function(
|
||||
plugin_name="TextBot", function_name="TextCompletion", prompt_template_config=prompt_template_config
|
||||
)
|
||||
|
||||
@@ -209,7 +210,7 @@ def _format_output(chat, banned_words) -> None:
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
kernel = sk.Kernel()
|
||||
kernel = Kernel()
|
||||
api_key, org_id = sk.openai_settings_from_dot_env()
|
||||
|
||||
print("Chat completion example:")
|
||||
|
||||
@@ -146,7 +146,7 @@ async def main():
|
||||
|
||||
http_client = httpx.AsyncClient()
|
||||
|
||||
plugin = await kernel.import_plugin_from_openai(
|
||||
plugin = await kernel.add_plugin_from_openai(
|
||||
plugin_name="AzureKeyVaultPlugin",
|
||||
plugin_str=openai_spec,
|
||||
execution_parameters=OpenAIFunctionExecutionParameters(
|
||||
|
||||
@@ -6,12 +6,11 @@ from semantic_kernel.kernel import Kernel
|
||||
|
||||
|
||||
async def main():
|
||||
|
||||
# This is an example of how to import a plugin from OpenAI and invoke a function from the plugin
|
||||
# It does not require authentication
|
||||
|
||||
kernel = Kernel()
|
||||
plugin = await kernel.import_plugin_from_openai(
|
||||
plugin = await kernel.add_plugin_from_openai(
|
||||
plugin_name="Klarna",
|
||||
plugin_url="https://www.klarna.com/.well-known/ai-plugin.json",
|
||||
)
|
||||
|
||||
@@ -31,7 +31,7 @@ async def main():
|
||||
|
||||
# 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")
|
||||
plugin = kernel.add_plugin(parent_directory=plugins_directory, plugin_name="FunPlugin")
|
||||
|
||||
arguments = KernelArguments(input="time travel to dinosaur age", style="super silly")
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ async def main():
|
||||
kernel.add_service(embedding_gen)
|
||||
|
||||
memory = SemanticTextMemory(storage=VolatileMemoryStore(), embeddings_generator=embedding_gen)
|
||||
kernel.import_plugin_from_object(TextMemoryPlugin(memory), "memory")
|
||||
kernel.add_plugin(TextMemoryPlugin(memory), "memory")
|
||||
|
||||
await memory.save_information(collection="generic", id="info1", text="My budget for 2024 is $100,000")
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ class EmailPlugin:
|
||||
Description: EmailPlugin provides a set of functions to send emails.
|
||||
|
||||
Usage:
|
||||
kernel.import_plugin_from_object(EmailPlugin(), plugin_name="email")
|
||||
kernel.add_plugin(EmailPlugin(), plugin_name="email")
|
||||
|
||||
Examples:
|
||||
{{email.SendEmail}} => Sends an email with the provided subject and body.
|
||||
|
||||
@@ -5,13 +5,8 @@ import asyncio
|
||||
from dotenv import dotenv_values
|
||||
|
||||
import semantic_kernel as sk
|
||||
from semantic_kernel.connectors.ai.open_ai import (
|
||||
AzureChatCompletion,
|
||||
AzureTextEmbedding,
|
||||
)
|
||||
from semantic_kernel.connectors.memory.azure_cognitive_search import (
|
||||
AzureCognitiveSearchMemoryStore,
|
||||
)
|
||||
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion, AzureTextEmbedding
|
||||
from semantic_kernel.connectors.memory.azure_cognitive_search import AzureCognitiveSearchMemoryStore
|
||||
from semantic_kernel.contents.chat_history import ChatHistory
|
||||
from semantic_kernel.core_plugins.text_memory_plugin import TextMemoryPlugin
|
||||
from semantic_kernel.memory.semantic_text_memory import SemanticTextMemory
|
||||
@@ -69,7 +64,7 @@ async def main() -> None:
|
||||
)
|
||||
|
||||
memory = SemanticTextMemory(storage=acs_connector, embeddings_generator=embedding_gen)
|
||||
kernel.import_plugin_from_object(TextMemoryPlugin(memory), "TextMemoryPlugin")
|
||||
kernel.add_plugin(TextMemoryPlugin(memory), "TextMemoryPlugin")
|
||||
|
||||
print("Populating memory...")
|
||||
await populate_memory(memory)
|
||||
@@ -96,11 +91,11 @@ Remember, just answer Grounded or Ungrounded or Unclear: """.strip()
|
||||
|
||||
user_input = "Do I live in Seattle?"
|
||||
print(f"Question: {user_input}")
|
||||
req_settings = kernel.get_service("dv").get_prompt_execution_settings_class()(service_id="dv")
|
||||
chat_func = kernel.create_function_from_prompt(
|
||||
req_settings = kernel.get_prompt_execution_settings_from_service_id(service_id="dv")
|
||||
chat_func = kernel.add_function(
|
||||
function_name="rag", plugin_name="RagPlugin", prompt=sk_prompt_rag, prompt_execution_settings=req_settings
|
||||
)
|
||||
self_critique_func = kernel.create_function_from_prompt(
|
||||
self_critique_func = kernel.add_function(
|
||||
function_name="self_critique_rag",
|
||||
plugin_name="RagPlugin",
|
||||
prompt=sk_prompt_rag_sc,
|
||||
|
||||
@@ -18,9 +18,9 @@ async def main():
|
||||
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_from_object(MathPlugin(), "math")
|
||||
kernel.import_plugin_from_object(TimePlugin(), "time")
|
||||
kernel.import_plugin_from_object(TextPlugin(), "text")
|
||||
kernel.add_plugin(MathPlugin(), "math")
|
||||
kernel.add_plugin(TimePlugin(), "time")
|
||||
kernel.add_plugin(TextPlugin(), "text")
|
||||
|
||||
# create an instance of sequential planner.
|
||||
planner = SequentialPlanner(service_id=service_id, kernel=kernel)
|
||||
|
||||
@@ -4,9 +4,7 @@ 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.core_plugins import TimePlugin
|
||||
from semantic_kernel.prompt_template.kernel_prompt_template import KernelPromptTemplate
|
||||
from semantic_kernel.prompt_template.prompt_template_config import PromptTemplateConfig
|
||||
|
||||
@@ -23,7 +21,7 @@ async def main():
|
||||
sk_oai.OpenAIChatCompletion(service_id=service_id, ai_model_id=model, api_key=api_key, org_id=org_id),
|
||||
)
|
||||
|
||||
kernel.import_plugin_from_object(TimePlugin(), "time")
|
||||
kernel.add_plugin(TimePlugin(), "time")
|
||||
|
||||
function_definition = """
|
||||
Today is: {{time.Date}}
|
||||
@@ -40,7 +38,7 @@ async def main():
|
||||
rendered_prompt = await prompt_template.render(kernel, arguments=None)
|
||||
print(rendered_prompt)
|
||||
|
||||
kind_of_day = kernel.create_function_from_prompt(
|
||||
kind_of_day = kernel.add_function(
|
||||
plugin_name="TimePlugin",
|
||||
template=function_definition,
|
||||
execution_settings=sk_oai.OpenAIChatPromptExecutionSettings(service_id=service_id, max_tokens=100),
|
||||
|
||||
@@ -151,13 +151,10 @@ def get_function_calling_object(
|
||||
if exclude_function:
|
||||
exclude_function = [function for function in exclude_function]
|
||||
result = []
|
||||
for (
|
||||
plugin_name,
|
||||
plugin,
|
||||
) in kernel.plugins.plugins.items():
|
||||
for plugin_name, plugin in kernel.plugins.items():
|
||||
if plugin_name in exclude_plugin or (include_plugin and plugin_name not in include_plugin):
|
||||
continue
|
||||
for function in plugin.functions.values():
|
||||
for function in plugin:
|
||||
if function.fully_qualified_name in exclude_function or (
|
||||
include_function and function.fully_qualified_name not in include_function
|
||||
):
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from semantic_kernel.exceptions.function_exceptions import PluginInitializationError
|
||||
|
||||
@@ -13,7 +14,7 @@ class OpenAIUtils:
|
||||
"""Utility functions for OpenAI plugins."""
|
||||
|
||||
@staticmethod
|
||||
def parse_openai_manifest_for_openapi_spec_url(plugin_json):
|
||||
def parse_openai_manifest_for_openapi_spec_url(plugin_json: dict[str, Any]) -> str:
|
||||
"""Extract the OpenAPI Spec URL from the plugin JSON."""
|
||||
|
||||
try:
|
||||
|
||||
@@ -3,6 +3,5 @@
|
||||
from semantic_kernel.connectors.openapi_plugin.openapi_function_execution_parameters import (
|
||||
OpenAPIFunctionExecutionParameters,
|
||||
)
|
||||
from semantic_kernel.connectors.openapi_plugin.openapi_manager import OpenAPIPlugin
|
||||
|
||||
__all__ = ["OpenAPIPlugin", "OpenAPIFunctionExecutionParameters"]
|
||||
__all__ = ["OpenAPIFunctionExecutionParameters"]
|
||||
|
||||
@@ -7,10 +7,13 @@ import logging
|
||||
import sys
|
||||
from typing import TYPE_CHECKING, Any, Callable, Dict, Mapping
|
||||
|
||||
from semantic_kernel.functions.kernel_function_from_method import KernelFunctionFromMethod
|
||||
|
||||
if sys.version_info >= (3, 9):
|
||||
from typing import Annotated
|
||||
else:
|
||||
from typing_extensions import Annotated
|
||||
|
||||
from urllib.parse import urljoin, urlparse, urlunparse
|
||||
|
||||
import aiohttp
|
||||
@@ -20,12 +23,9 @@ from openapi_core.contrib.requests import RequestsOpenAPIRequest
|
||||
from openapi_core.exceptions import OpenAPIError
|
||||
from prance import ResolvingParser
|
||||
|
||||
from semantic_kernel.connectors.ai.open_ai.const import (
|
||||
USER_AGENT,
|
||||
)
|
||||
from semantic_kernel.connectors.ai.open_ai.const import USER_AGENT
|
||||
from semantic_kernel.exceptions import ServiceInvalidRequestError
|
||||
from semantic_kernel.functions.kernel_function_decorator import kernel_function
|
||||
from semantic_kernel.functions.kernel_plugin import KernelPlugin
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from semantic_kernel.connectors.openai_plugin.openai_function_execution_parameters import (
|
||||
@@ -211,7 +211,7 @@ class OpenApiParser:
|
||||
def create_rest_api_operations(
|
||||
self,
|
||||
parsed_document: Any,
|
||||
execution_settings: "OpenAIFunctionExecutionParameters" | "OpenAPIFunctionExecutionParameters" | None = None,
|
||||
execution_settings: "OpenAIFunctionExecutionParameters | OpenAPIFunctionExecutionParameters | None" = None,
|
||||
) -> Dict[str, RestApiOperation]:
|
||||
"""Create the REST API Operations from the parsed OpenAPI document.
|
||||
|
||||
@@ -303,69 +303,61 @@ class OpenApiRunner:
|
||||
return await response.text()
|
||||
|
||||
|
||||
class OpenAPIPlugin:
|
||||
@staticmethod
|
||||
def create(
|
||||
plugin_name: str,
|
||||
openapi_document_path: str,
|
||||
execution_settings: "OpenAIFunctionExecutionParameters" | "OpenAPIFunctionExecutionParameters" | None = None,
|
||||
) -> KernelPlugin:
|
||||
"""Creates an OpenAPI plugin
|
||||
def create_functions_from_openapi(
|
||||
plugin_name: str,
|
||||
openapi_document_path: str,
|
||||
execution_settings: "OpenAIFunctionExecutionParameters | OpenAPIFunctionExecutionParameters | None" = None,
|
||||
) -> list[KernelFunctionFromMethod]:
|
||||
"""Creates the functions from OpenAPI document.
|
||||
|
||||
Args:
|
||||
plugin_name: The name of the plugin
|
||||
openapi_document_path: The OpenAPI document path, it must be a file path to the spec.
|
||||
execution_settings: The execution settings
|
||||
Args:
|
||||
plugin_name: The name of the plugin
|
||||
openapi_document_path: The OpenAPI document path, it must be a file path to the spec.
|
||||
execution_settings: The execution settings
|
||||
|
||||
Returns:
|
||||
The KernelPlugin
|
||||
"""
|
||||
parser = OpenApiParser()
|
||||
parsed_doc = parser.parse(openapi_document_path)
|
||||
operations = parser.create_rest_api_operations(parsed_doc, execution_settings=execution_settings)
|
||||
Returns:
|
||||
list[KernelFunctionFromMethod]: the operations as functions
|
||||
"""
|
||||
parser = OpenApiParser()
|
||||
parsed_doc = parser.parse(openapi_document_path)
|
||||
operations = parser.create_rest_api_operations(parsed_doc, execution_settings=execution_settings)
|
||||
|
||||
auth_callback = None
|
||||
if execution_settings and execution_settings.auth_callback:
|
||||
auth_callback = execution_settings.auth_callback
|
||||
openapi_runner = OpenApiRunner(parsed_openapi_document=parsed_doc, auth_callback=auth_callback)
|
||||
auth_callback = None
|
||||
if execution_settings and execution_settings.auth_callback:
|
||||
auth_callback = execution_settings.auth_callback
|
||||
openapi_runner = OpenApiRunner(parsed_openapi_document=parsed_doc, auth_callback=auth_callback)
|
||||
|
||||
plugin = {}
|
||||
return [
|
||||
_create_function_from_operation(openapi_runner, operation, plugin_name) for operation in operations.values()
|
||||
]
|
||||
|
||||
def create_run_operation_function(runner: OpenApiRunner, operation: RestApiOperation):
|
||||
@kernel_function(
|
||||
description=operation.summary if operation.summary else operation.description,
|
||||
name=operation.id,
|
||||
)
|
||||
async def run_openapi_operation(
|
||||
path_params: Annotated[dict | str | None, "A dictionary of path parameters"] = None,
|
||||
query_params: Annotated[dict | str | None, "A dictionary of query parameters"] = None,
|
||||
headers: Annotated[dict | str | None, "A dictionary of headers"] = None,
|
||||
request_body: Annotated[dict | str | None, "A dictionary of the request body"] = None,
|
||||
) -> str:
|
||||
response = await runner.run_operation(
|
||||
operation,
|
||||
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
|
||||
|
||||
return run_openapi_operation
|
||||
def _create_function_from_operation(runner: OpenApiRunner, operation: RestApiOperation, plugin_name: str | None = None):
|
||||
logger.info(f"Registering OpenAPI operation: {plugin_name}.{operation.id}")
|
||||
|
||||
for operation_id, operation in operations.items():
|
||||
logger.info(f"Registering OpenAPI operation: {plugin_name}.{operation_id}")
|
||||
plugin[operation_id] = create_run_operation_function(openapi_runner, operation)
|
||||
return plugin
|
||||
@kernel_function(
|
||||
description=operation.summary if operation.summary else operation.description,
|
||||
name=operation.id,
|
||||
)
|
||||
async def run_openapi_operation(
|
||||
path_params: Annotated[dict | str | None, "A dictionary of path parameters"] = None,
|
||||
query_params: Annotated[dict | str | None, "A dictionary of query parameters"] = None,
|
||||
headers: Annotated[dict | str | None, "A dictionary of headers"] = None,
|
||||
request_body: Annotated[dict | str | None, "A dictionary of the request body"] = None,
|
||||
) -> str:
|
||||
response = await runner.run_operation(
|
||||
operation,
|
||||
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
|
||||
|
||||
return KernelFunctionFromMethod(run_openapi_operation, plugin_name=plugin_name)
|
||||
|
||||
@@ -45,7 +45,7 @@ class ConversationSummaryPlugin:
|
||||
:param return_key: The key to use for the return value.
|
||||
"""
|
||||
self.return_key = return_key
|
||||
self._summarizeConversationFunction = kernel.create_function_from_prompt(
|
||||
self._summarizeConversationFunction = kernel.add_function(
|
||||
prompt=ConversationSummaryPlugin._summarize_conversation_prompt_template,
|
||||
plugin_name=ConversationSummaryPlugin.__name__,
|
||||
function_name="SummarizeConversation",
|
||||
@@ -73,9 +73,7 @@ class ConversationSummaryPlugin:
|
||||
: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 (
|
||||
aggregate_chunked_results,
|
||||
)
|
||||
from semantic_kernel.text.function_extension import aggregate_chunked_results
|
||||
|
||||
lines = text_chunker._split_text_lines(input, ConversationSummaryPlugin._max_tokens, True)
|
||||
paragraphs = text_chunker._split_text_paragraph(lines, ConversationSummaryPlugin._max_tokens)
|
||||
|
||||
@@ -7,7 +7,6 @@ from semantic_kernel.functions.kernel_function_decorator import kernel_function
|
||||
from semantic_kernel.functions.kernel_function_metadata import KernelFunctionMetadata
|
||||
from semantic_kernel.functions.kernel_parameter_metadata import KernelParameterMetadata
|
||||
from semantic_kernel.functions.kernel_plugin import KernelPlugin
|
||||
from semantic_kernel.functions.kernel_plugin_collection import KernelPluginCollection
|
||||
|
||||
__all__ = [
|
||||
"FunctionResult",
|
||||
@@ -17,5 +16,4 @@ __all__ = [
|
||||
"KernelFunctionMetadata",
|
||||
"KernelParameterMetadata",
|
||||
"KernelPlugin",
|
||||
"KernelPluginCollection",
|
||||
]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
|
||||
@@ -9,7 +10,7 @@ if TYPE_CHECKING:
|
||||
class KernelArguments(dict):
|
||||
def __init__(
|
||||
self,
|
||||
settings: Optional[Union["PromptExecutionSettings", List["PromptExecutionSettings"]]] = None,
|
||||
settings: "PromptExecutionSettings" | list["PromptExecutionSettings"] | None = None,
|
||||
**kwargs: Any,
|
||||
):
|
||||
"""Initializes a new instance of the KernelArguments class,
|
||||
@@ -19,11 +20,11 @@ class KernelArguments(dict):
|
||||
just adds the execution_settings as a dict, with service_id and the settings.
|
||||
|
||||
Arguments:
|
||||
settings {Optional[Union[PromptExecutionSettings, List[PromptExecutionSettings]]]} --
|
||||
settings (PromptExecutionSettings | List[PromptExecutionSettings] | None) --
|
||||
The settings for the execution.
|
||||
If a list is given, make sure all items in the list have a unique service_id
|
||||
as that is used as the key for the dict.
|
||||
**kwargs {Dict[str, Any]} -- The arguments for the function invocation, works similar to a regular dict.
|
||||
**kwargs (dict[str, Any]) -- The arguments for the function invocation, works similar to a regular dict.
|
||||
"""
|
||||
super().__init__(**kwargs)
|
||||
settings_dict = {}
|
||||
@@ -32,4 +33,4 @@ class KernelArguments(dict):
|
||||
settings_dict = {s.service_id: s for s in settings}
|
||||
else:
|
||||
settings_dict = {settings.service_id: settings}
|
||||
self.execution_settings: Optional[Dict[str, "PromptExecutionSettings"]] = settings_dict
|
||||
self.execution_settings: dict[str, "PromptExecutionSettings"] | None = settings_dict
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from abc import abstractmethod
|
||||
from copy import copy, deepcopy
|
||||
from typing import TYPE_CHECKING, Any, AsyncIterable, Callable, Dict, List, Optional, Union
|
||||
|
||||
from semantic_kernel.functions.function_result import FunctionResult
|
||||
@@ -227,3 +229,18 @@ class KernelFunction(KernelBaseModel):
|
||||
except Exception as e:
|
||||
logger.error(f"Error occurred while invoking function {self.name}: {e}")
|
||||
yield FunctionResult(function=self.metadata, value=None, metadata={"exception": e, "arguments": arguments})
|
||||
|
||||
def function_copy(self, plugin_name: str | None = None) -> "KernelFunction":
|
||||
"""Copy the function, can also override the plugin_name.
|
||||
|
||||
Args:
|
||||
plugin_name (str): The new plugin name.
|
||||
|
||||
Returns:
|
||||
KernelFunction: The copied function.
|
||||
"""
|
||||
cop: KernelFunction = copy(self)
|
||||
cop.metadata = deepcopy(self.metadata)
|
||||
if plugin_name:
|
||||
cop.metadata.plugin_name = plugin_name
|
||||
return cop
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
from typing import TYPE_CHECKING, Any, AsyncIterable, Dict, List, Optional, Union
|
||||
|
||||
import yaml
|
||||
from pydantic import Field, ValidationError, model_validator
|
||||
|
||||
from semantic_kernel.connectors.ai.chat_completion_client_base import ChatCompletionClientBase
|
||||
@@ -30,6 +33,8 @@ if TYPE_CHECKING:
|
||||
|
||||
logger: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
PROMPT_FILE_NAME = "skprompt.txt"
|
||||
CONFIG_FILE_NAME = "config.json"
|
||||
PROMPT_RETURN_PARAM = KernelParameterMetadata(
|
||||
name="return",
|
||||
description="The completion result",
|
||||
@@ -48,7 +53,7 @@ class KernelFunctionFromPrompt(KernelFunction):
|
||||
def __init__(
|
||||
self,
|
||||
function_name: str,
|
||||
plugin_name: str,
|
||||
plugin_name: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
prompt: Optional[str] = None,
|
||||
template_format: TEMPLATE_FORMAT_TYPES = KERNEL_TEMPLATE_FORMAT_NAME,
|
||||
@@ -319,3 +324,80 @@ through prompt_template_config or in the prompt_template."
|
||||
if parameter.name not in arguments and parameter.default not in {None, "", False, 0}:
|
||||
arguments[parameter.name] = parameter.default
|
||||
return arguments
|
||||
|
||||
@classmethod
|
||||
def from_yaml(cls, yaml_str: str, plugin_name: str | None = None) -> "KernelFunctionFromPrompt":
|
||||
"""Creates a new instance of the KernelFunctionFromPrompt class from a YAML string."""
|
||||
try:
|
||||
data = yaml.safe_load(yaml_str)
|
||||
except yaml.YAMLError as exc: # pragma: no cover
|
||||
raise FunctionInitializationError(f"Invalid YAML content: {yaml_str}, error: {exc}") from exc
|
||||
|
||||
if not isinstance(data, dict):
|
||||
raise FunctionInitializationError(f"The YAML content must represent a dictionary, got {yaml_str}")
|
||||
|
||||
try:
|
||||
prompt_template_config = PromptTemplateConfig(**data)
|
||||
except ValidationError as exc:
|
||||
raise FunctionInitializationError(
|
||||
f"Error initializing PromptTemplateConfig: {exc} from yaml data: {data}"
|
||||
) from exc
|
||||
return cls(
|
||||
function_name=prompt_template_config.name,
|
||||
plugin_name=plugin_name,
|
||||
description=prompt_template_config.description,
|
||||
prompt_template_config=prompt_template_config,
|
||||
template_format=prompt_template_config.template_format,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_directory(cls, path: str, plugin_name: str | None = None) -> "KernelFunctionFromPrompt":
|
||||
"""Creates a new instance of the KernelFunctionFromPrompt class from a directory.
|
||||
|
||||
The directory needs to contain:
|
||||
- A prompt file named `skprompt.txt`
|
||||
- A config file named `config.json`
|
||||
|
||||
Returns:
|
||||
KernelFunctionFromPrompt: The kernel function from prompt
|
||||
"""
|
||||
prompt_path = os.path.join(path, PROMPT_FILE_NAME)
|
||||
config_path = os.path.join(path, CONFIG_FILE_NAME)
|
||||
prompt_exists = os.path.exists(prompt_path)
|
||||
config_exists = os.path.exists(config_path)
|
||||
if not config_exists and not prompt_exists:
|
||||
raise FunctionInitializationError(
|
||||
f"{PROMPT_FILE_NAME} and {CONFIG_FILE_NAME} files are required to create a "
|
||||
f"function from a directory, path: {str(path)}."
|
||||
)
|
||||
elif not config_exists:
|
||||
raise FunctionInitializationError(
|
||||
f"{CONFIG_FILE_NAME} files are required to create a function from a directory, "
|
||||
f"path: {str(path)}, prompt file is there."
|
||||
)
|
||||
elif not prompt_exists:
|
||||
raise FunctionInitializationError(
|
||||
f"{PROMPT_FILE_NAME} files are required to create a function from a directory, "
|
||||
f"path: {str(path)}, config file is there."
|
||||
)
|
||||
|
||||
function_name = os.path.basename(path)
|
||||
|
||||
with open(config_path, "r") as config_file:
|
||||
prompt_template_config = PromptTemplateConfig.from_json(config_file.read())
|
||||
prompt_template_config.name = function_name
|
||||
|
||||
with open(prompt_path, "r") as prompt_file:
|
||||
prompt_template_config.template = prompt_file.read()
|
||||
|
||||
prompt_template = TEMPLATE_FORMAT_MAP[prompt_template_config.template_format]( # type: ignore
|
||||
prompt_template_config=prompt_template_config
|
||||
)
|
||||
return cls(
|
||||
function_name=function_name,
|
||||
plugin_name=plugin_name,
|
||||
prompt_template=prompt_template,
|
||||
prompt_template_config=prompt_template_config,
|
||||
template_format=prompt_template_config.template_format,
|
||||
description=prompt_template_config.description,
|
||||
)
|
||||
|
||||
@@ -1,129 +1,523 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
import inspect
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from typing import TYPE_CHECKING, Dict, List, Optional, Union
|
||||
from collections.abc import Callable, Iterable
|
||||
from glob import glob
|
||||
from types import MethodType
|
||||
from typing import TYPE_CHECKING, Any, ItemsView
|
||||
|
||||
from semantic_kernel.connectors.openapi_plugin.openapi_manager import create_functions_from_openapi
|
||||
from semantic_kernel.exceptions.function_exceptions import FunctionInitializationError
|
||||
|
||||
if sys.version_info >= (3, 9):
|
||||
from typing import Annotated
|
||||
from typing import Annotated # pragma: no cover
|
||||
else:
|
||||
from typing_extensions import Annotated
|
||||
from typing_extensions import Annotated # pragma: no cover
|
||||
|
||||
import httpx
|
||||
from pydantic import Field, StringConstraints
|
||||
|
||||
from semantic_kernel.exceptions import FunctionInvalidNameError
|
||||
from semantic_kernel.connectors.openai_plugin.openai_authentication_config import OpenAIAuthenticationConfig
|
||||
from semantic_kernel.connectors.openai_plugin.openai_function_execution_parameters import (
|
||||
OpenAIFunctionExecutionParameters,
|
||||
)
|
||||
from semantic_kernel.connectors.openai_plugin.openai_utils import OpenAIUtils
|
||||
from semantic_kernel.connectors.utils.document_loader import DocumentLoader
|
||||
from semantic_kernel.exceptions import PluginInitializationError
|
||||
from semantic_kernel.functions.kernel_function import KernelFunction
|
||||
from semantic_kernel.functions.kernel_function_from_method import KernelFunctionFromMethod
|
||||
from semantic_kernel.functions.kernel_function_from_prompt import KernelFunctionFromPrompt
|
||||
from semantic_kernel.functions.types import KERNEL_FUNCTION_TYPE
|
||||
from semantic_kernel.kernel_pydantic import KernelBaseModel
|
||||
from semantic_kernel.utils.validation import PLUGIN_NAME_REGEX
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from semantic_kernel.functions.kernel_function import KernelFunction
|
||||
from semantic_kernel.connectors.openapi_plugin.openapi_function_execution_parameters import (
|
||||
OpenAPIFunctionExecutionParameters,
|
||||
)
|
||||
from semantic_kernel.functions.kernel_function_metadata import KernelFunctionMetadata
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class KernelPlugin(KernelBaseModel):
|
||||
"""
|
||||
Represents a Kernel Plugin with functions.
|
||||
|
||||
This class behaves mostly like a dictionary, with functions as values and their names as keys.
|
||||
When you add a function, through `.set` or `__setitem__`, the function is copied, the metadata is deep-copied
|
||||
and the name of the plugin is set in the metadata and added to the dict of functions.
|
||||
This is done in the same way as a normal dict, so a existing key will be overwritten.
|
||||
|
||||
Attributes:
|
||||
name (str): The name of the plugin. The name can be upper/lower
|
||||
case letters and underscores.
|
||||
description (str): The description of the plugin.
|
||||
functions (Dict[str, KernelFunction]): The functions in the plugin,
|
||||
indexed by their name.
|
||||
|
||||
Methods:
|
||||
set, __setitem__ (key: str, value: KernelFunction): Set a function in the plugin.
|
||||
get (key: str, default: KernelFunction | None = None): Get a function from the plugin.
|
||||
__getitem__ (key: str): Get a function from the plugin.
|
||||
__contains__ (key: str): Check if a function is in the plugin.
|
||||
__iter__ (): Iterate over the functions in the plugin.
|
||||
update(*args: Any, **kwargs: Any): Update the plugin with the functions from another.
|
||||
setdefault(key: str, value: KernelFunction | None): Set a default value for a key.
|
||||
get_functions_metadata(): Get the metadata for the functions in the plugin.
|
||||
|
||||
Class methods:
|
||||
from_object(plugin_name: str, plugin_instance: Any | dict[str, Any], description: str | None = None):
|
||||
Create a plugin from a existing object, like a custom class with annotated functions.
|
||||
from_directory(plugin_name: str, parent_directory: str, description: str | None = None):
|
||||
Create a plugin from a directory, parsing:
|
||||
.py files, .yaml files and directories with skprompt.txt and config.json files.
|
||||
from_openapi(
|
||||
plugin_name: str,
|
||||
openapi_document_path: str,
|
||||
execution_settings: OpenAPIFunctionExecutionParameters | None = None,
|
||||
description: str | None = None):
|
||||
Create a plugin from an OpenAPI document.
|
||||
from_openai(
|
||||
plugin_name: str,
|
||||
plugin_url: str | None = None,
|
||||
plugin_str: str | None = None,
|
||||
execution_parameters: OpenAIFunctionExecutionParameters | None = None,
|
||||
description: str | None = None):
|
||||
Create a plugin from the Open AI manifest.
|
||||
|
||||
"""
|
||||
|
||||
name: Annotated[str, StringConstraints(pattern=PLUGIN_NAME_REGEX, min_length=1)]
|
||||
description: Optional[str] = Field(default=None)
|
||||
functions: Optional[Dict[str, "KernelFunction"]] = Field(default_factory=dict)
|
||||
description: str | None = None
|
||||
functions: dict[str, KernelFunction] = Field(default_factory=dict)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
description: Optional[str] = None,
|
||||
functions: Optional[Union[List["KernelFunction"], Dict[str, "KernelFunction"]]] = None,
|
||||
description: str | None = None,
|
||||
functions: (
|
||||
KERNEL_FUNCTION_TYPE
|
||||
| KernelPlugin
|
||||
| list[KERNEL_FUNCTION_TYPE | KernelPlugin]
|
||||
| dict[str, KERNEL_FUNCTION_TYPE]
|
||||
| None
|
||||
) = None,
|
||||
):
|
||||
"""
|
||||
Initialize a new instance of the KernelPlugin class
|
||||
"""Create a KernelPlugin
|
||||
|
||||
Args:
|
||||
name (str): The name of the plugin.
|
||||
description (Optional[str]): The description of the plugin.
|
||||
functions (List[KernelFunction]): The functions in the plugin.
|
||||
Attributes:
|
||||
name (str): The name of the plugin. The name can be upper/lower
|
||||
case letters and underscores.
|
||||
description (str, optional): The description of the plugin.
|
||||
functions (
|
||||
KernelFunction |
|
||||
Callable |
|
||||
list[KernelFunction | Callable | KernelPlugin] |
|
||||
dict[str, KernelFunction | Callable] |
|
||||
KernelPlugin |
|
||||
None):
|
||||
The functions in the plugin, will be rewritten to a dictionary of functions.
|
||||
|
||||
Raises:
|
||||
ValueError: If the functions list contains duplicate function names.
|
||||
ValueError: If the functions are not of the correct type.
|
||||
PydanticError: If the name is not a valid plugin name.
|
||||
"""
|
||||
functions_dict = {}
|
||||
if functions is not None:
|
||||
if isinstance(functions, list):
|
||||
for function in functions:
|
||||
if function.name in functions_dict:
|
||||
raise FunctionInvalidNameError(f"Duplicate function name detected: {function.name}")
|
||||
functions_dict[function.name] = function
|
||||
super().__init__(
|
||||
name=name,
|
||||
description=description,
|
||||
functions=self._validate_functions(functions=functions, plugin_name=name),
|
||||
)
|
||||
|
||||
# region Dict-like methods
|
||||
|
||||
def __setitem__(self, key: str, value: KernelFunction) -> None:
|
||||
self.functions[key] = KernelPlugin._parse_or_copy(value, self.name)
|
||||
|
||||
def set(self, key: str, value: KernelFunction) -> None:
|
||||
"""Set a function in the plugin.
|
||||
|
||||
Args:
|
||||
key (str): The name of the function.
|
||||
value (KernelFunction): The function to set.
|
||||
|
||||
"""
|
||||
self[key] = value
|
||||
|
||||
def __getitem__(self, key: str) -> KernelFunction:
|
||||
return self.functions[key]
|
||||
|
||||
def get(self, key: str, default: KernelFunction | None = None) -> KernelFunction | None:
|
||||
return self.functions.get(key, default)
|
||||
|
||||
def update(self, *args: Any, **kwargs: KernelFunction) -> None:
|
||||
"""Update the plugin with the functions from another.
|
||||
|
||||
Args:
|
||||
*args: The functions to update the plugin with, can be a dict, list or KernelPlugin.
|
||||
**kwargs: The kernel functions to update the plugin with.
|
||||
|
||||
"""
|
||||
if len(args) > 1:
|
||||
raise TypeError("update expected at most 1 arguments, got %d" % len(args))
|
||||
if args:
|
||||
other = args[0]
|
||||
if isinstance(other, KernelPlugin):
|
||||
other = other.functions
|
||||
if not isinstance(other, (dict, list)):
|
||||
raise TypeError(f"Expected dict, KernelPlugin or list as arg, got {type(other)}")
|
||||
if isinstance(other, dict):
|
||||
for key in other:
|
||||
self[key] = other[key]
|
||||
else:
|
||||
functions_dict = functions
|
||||
super().__init__(name=name, description=description, functions=functions_dict)
|
||||
for item in other:
|
||||
if isinstance(item, (KernelFunction, Callable)):
|
||||
item = KernelPlugin._parse_or_copy(item, self.name)
|
||||
self[item.name] = item
|
||||
elif isinstance(item, KernelPlugin):
|
||||
for key in item.functions:
|
||||
self[key] = item.functions[key]
|
||||
if kwargs:
|
||||
for key in kwargs:
|
||||
self[key] = kwargs[key]
|
||||
|
||||
def __len__(self) -> int:
|
||||
"""
|
||||
Gets the number of functions in the plugin.
|
||||
def setdefault(self, key: str, value: KernelFunction | None = None):
|
||||
if key not in self.functions:
|
||||
if value is None:
|
||||
raise ValueError("Value must be provided for new key.")
|
||||
self[key] = value
|
||||
return self[key]
|
||||
|
||||
Returns:
|
||||
The number of functions in the plugin.
|
||||
def __iter__(self) -> Iterable[KernelFunction]:
|
||||
for function in self.functions.values():
|
||||
yield function
|
||||
|
||||
"""
|
||||
return len(self.functions)
|
||||
def __contains__(self, key: str) -> bool:
|
||||
return key in self.functions
|
||||
|
||||
def __contains__(self, function_name: str) -> bool:
|
||||
"""
|
||||
Checks if the plugin contains a function with the specified name.
|
||||
# endregion
|
||||
# region Properties
|
||||
|
||||
Args:
|
||||
function_name (str): The name of the function.
|
||||
|
||||
Returns:
|
||||
True if the plugin contains a function with the specified name, False otherwise.
|
||||
"""
|
||||
return function_name in self.functions.keys()
|
||||
|
||||
def __getitem__(self, name: str) -> "KernelFunction":
|
||||
"""Define the [] operator for the plugin
|
||||
|
||||
Args:
|
||||
name (str): The name of the function to retrieve.
|
||||
|
||||
Returns:
|
||||
The function if it exists, None otherwise.
|
||||
|
||||
Raises:
|
||||
KeyError: If the function does not exist.
|
||||
"""
|
||||
if name not in self.functions:
|
||||
raise KeyError(f"Function {name} not found.")
|
||||
return self.functions[name]
|
||||
|
||||
@classmethod
|
||||
def from_functions(
|
||||
cls, functions: List["KernelFunction"], plugin_name: str, description: Optional[str] = None
|
||||
) -> "KernelPlugin":
|
||||
"""
|
||||
Creates a KernelPlugin from a KernelFunction instance.
|
||||
|
||||
Args:
|
||||
functions (List[KernelFunction]): The functions to create the plugin from.
|
||||
plugin_name (Optional[str]): The name of the plugin. If not specified,
|
||||
the name of the function will be used.
|
||||
description (Optional[str]): The description of the plugin.
|
||||
|
||||
Returns:
|
||||
A KernelPlugin instance.
|
||||
"""
|
||||
return cls(name=plugin_name, description=description, functions=functions)
|
||||
|
||||
def get_functions_metadata(self) -> List["KernelFunctionMetadata"]:
|
||||
def get_functions_metadata(self) -> list["KernelFunctionMetadata"]:
|
||||
"""
|
||||
Get the metadata for the functions in the plugin.
|
||||
|
||||
Returns:
|
||||
A list of KernelFunctionMetadata instances.
|
||||
"""
|
||||
return [func.metadata for func in self.functions.values()]
|
||||
return [func.metadata for func in self]
|
||||
|
||||
# endregion
|
||||
# region Class Methods
|
||||
|
||||
@classmethod
|
||||
def from_object(
|
||||
cls, plugin_name: str, plugin_instance: Any | dict[str, Any], description: str | None = None
|
||||
) -> "KernelPlugin":
|
||||
"""
|
||||
Creates a plugin that wraps the specified target object and imports it into the kernel's plugin collection
|
||||
|
||||
Args:
|
||||
plugin_instance (Any | dict[str, Any]): The plugin instance. This can be a custom class or a
|
||||
dictionary of classes that contains methods with the kernel_function decorator for one or
|
||||
several methods. See `TextMemoryPlugin` as an example.
|
||||
plugin_name (str): The name of the plugin. Allows chars: upper, lower ASCII and underscores.
|
||||
|
||||
Returns:
|
||||
KernelPlugin: The imported plugin of type KernelPlugin.
|
||||
"""
|
||||
functions: list[KernelFunction] = []
|
||||
candidates: list[tuple[str, MethodType]] | ItemsView[str, Any] = []
|
||||
|
||||
if isinstance(plugin_instance, dict):
|
||||
candidates = plugin_instance.items()
|
||||
else:
|
||||
candidates = inspect.getmembers(plugin_instance, inspect.ismethod)
|
||||
# Read every method from the plugin instance
|
||||
functions = [
|
||||
KernelFunctionFromMethod(method=candidate, plugin_name=plugin_name)
|
||||
for _, candidate in candidates
|
||||
if hasattr(candidate, "__kernel_function__")
|
||||
]
|
||||
return cls(name=plugin_name, description=description, functions=functions) # type: ignore
|
||||
|
||||
@classmethod
|
||||
def from_directory(
|
||||
cls,
|
||||
plugin_name: str,
|
||||
parent_directory: str,
|
||||
description: str | None = None,
|
||||
class_init_arguments: dict[str, dict[str, Any]] | None = None,
|
||||
) -> "KernelPlugin":
|
||||
"""Create a plugin from a specified directory.
|
||||
|
||||
This method does not recurse into subdirectories beyond one level deep from the specified plugin directory.
|
||||
For YAML files, function names are extracted from the content of the YAML files themselves (the name property).
|
||||
For directories, the function name is assumed to be the name of the directory. Each KernelFunction object is
|
||||
initialized with data parsed from the associated files and added to a list of functions that are then assigned
|
||||
to the created KernelPlugin object.
|
||||
A .py file is parsed and a plugin created,
|
||||
the functions within as then combined with any other functions found.
|
||||
The python file needs to contain a class with one or more kernel_function decorated methods.
|
||||
If this class has a `__init__` method, it will be called with the arguments provided in the
|
||||
`class_init_arguments` dictionary, the key needs to be the same as the name of the class,
|
||||
with the value being a dictionary of arguments to pass to the class (using kwargs).
|
||||
|
||||
Example:
|
||||
Assuming a plugin directory structure as follows:
|
||||
MyPlugins/
|
||||
|--- pluginA.yaml
|
||||
|--- pluginB.yaml
|
||||
|--- native_function.py
|
||||
|--- Directory1/
|
||||
|--- skprompt.txt
|
||||
|--- config.json
|
||||
|--- Directory2/
|
||||
|--- skprompt.txt
|
||||
|--- config.json
|
||||
|
||||
Calling `KernelPlugin.from_directory("MyPlugins", "/path/to")` will create a KernelPlugin object named
|
||||
"MyPlugins", containing KernelFunction objects for `pluginA.yaml`, `pluginB.yaml`,
|
||||
`Directory1`, and `Directory2`, each initialized with their respective configurations.
|
||||
And functions for anything within native_function.py.
|
||||
|
||||
Args:
|
||||
plugin_name (str): The name of the plugin, this is the name of the directory within the parent directory
|
||||
parent_directory (str): The parent directory path where the plugin directory resides
|
||||
description (str | None): The description of the plugin
|
||||
class_init_arguments (dict[str, dict[str, Any]] | None): The class initialization arguments
|
||||
|
||||
Returns:
|
||||
KernelPlugin: The created plugin of type KernelPlugin.
|
||||
|
||||
Raises:
|
||||
PluginInitializationError: If the plugin directory does not exist.
|
||||
PluginInvalidNameError: If the plugin name is invalid.
|
||||
"""
|
||||
plugin_directory = os.path.abspath(os.path.join(parent_directory, plugin_name))
|
||||
if not os.path.exists(plugin_directory):
|
||||
raise PluginInitializationError(f"Plugin directory does not exist: {plugin_name}")
|
||||
|
||||
functions: list[KernelFunction] = []
|
||||
for object in glob(os.path.join(plugin_directory, "*")):
|
||||
logger.debug(f"Found object: {object}")
|
||||
if os.path.isdir(object):
|
||||
if os.path.basename(object).startswith("__"):
|
||||
continue
|
||||
try:
|
||||
functions.append(KernelFunctionFromPrompt.from_directory(path=object))
|
||||
except FunctionInitializationError:
|
||||
logger.warning(f"Failed to create function from directory: {object}")
|
||||
elif object.endswith(".yaml") or object.endswith(".yml"):
|
||||
with open(object, "r") as file:
|
||||
try:
|
||||
functions.append(KernelFunctionFromPrompt.from_yaml(file.read()))
|
||||
except FunctionInitializationError:
|
||||
logger.warning(f"Failed to create function from YAML file: {object}")
|
||||
elif object.endswith(".py"):
|
||||
try:
|
||||
functions.extend(
|
||||
cls.from_python_file(
|
||||
plugin_name=plugin_name,
|
||||
py_file=object,
|
||||
description=description,
|
||||
class_init_arguments=class_init_arguments,
|
||||
)
|
||||
)
|
||||
except PluginInitializationError:
|
||||
logger.warning(f"Failed to create function from Python file: {object}")
|
||||
else:
|
||||
logger.warning(f"Unknown file found: {object}")
|
||||
if not functions:
|
||||
raise PluginInitializationError(f"No functions found in folder: {parent_directory}/{plugin_name}")
|
||||
return cls(name=plugin_name, description=description, functions=functions)
|
||||
|
||||
@classmethod
|
||||
def from_openapi(
|
||||
cls,
|
||||
plugin_name: str,
|
||||
openapi_document_path: str,
|
||||
execution_settings: "OpenAPIFunctionExecutionParameters | None" = None,
|
||||
description: str | None = None,
|
||||
) -> "KernelPlugin":
|
||||
"""Create a plugin from an OpenAPI document.
|
||||
|
||||
Args:
|
||||
plugin_name (str): The name of the plugin
|
||||
plugin_url (str | None): The URL of the plugin
|
||||
plugin_str (str | None): The JSON string of the plugin
|
||||
execution_parameters (OpenAIFunctionExecutionParameters | None): The execution parameters
|
||||
description (str | None): The description of the plugin
|
||||
|
||||
Returns:
|
||||
KernelPlugin: The created plugin
|
||||
|
||||
Raises:
|
||||
PluginInitializationError: if the plugin URL or plugin JSON/YAML is not provided
|
||||
"""
|
||||
|
||||
if not openapi_document_path:
|
||||
raise PluginInitializationError("OpenAPI document path is required.")
|
||||
|
||||
return cls(
|
||||
name=plugin_name,
|
||||
description=description,
|
||||
functions=create_functions_from_openapi(
|
||||
plugin_name=plugin_name,
|
||||
openapi_document_path=openapi_document_path,
|
||||
execution_settings=execution_settings,
|
||||
),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def from_openai(
|
||||
cls,
|
||||
plugin_name: str,
|
||||
plugin_url: str | None = None,
|
||||
plugin_str: str | None = None,
|
||||
execution_parameters: OpenAIFunctionExecutionParameters | None = None,
|
||||
description: str | None = None,
|
||||
) -> "KernelPlugin":
|
||||
"""Create a plugin from the Open AI manifest.
|
||||
|
||||
Args:
|
||||
plugin_name (str): The name of the plugin
|
||||
plugin_url (str | None): The URL of the plugin
|
||||
plugin_str (str | None): The JSON string of the plugin
|
||||
execution_parameters (OpenAIFunctionExecutionParameters | None): The execution parameters
|
||||
|
||||
Returns:
|
||||
KernelPlugin: The created plugin
|
||||
|
||||
Raises:
|
||||
PluginInitializationError: if the plugin URL or plugin JSON/YAML is not provided
|
||||
"""
|
||||
|
||||
if execution_parameters is None:
|
||||
execution_parameters = OpenAIFunctionExecutionParameters()
|
||||
|
||||
if plugin_str is not None:
|
||||
# Load plugin from the provided JSON string/YAML string
|
||||
openai_manifest = plugin_str
|
||||
elif plugin_url is not None:
|
||||
# Load plugin from the URL
|
||||
http_client = execution_parameters.http_client if execution_parameters.http_client else httpx.AsyncClient()
|
||||
openai_manifest = await DocumentLoader.from_uri(
|
||||
url=plugin_url, http_client=http_client, auth_callback=None, user_agent=execution_parameters.user_agent
|
||||
)
|
||||
else:
|
||||
raise PluginInitializationError("Either plugin_url or plugin_json must be provided.")
|
||||
|
||||
try:
|
||||
plugin_json = json.loads(openai_manifest)
|
||||
except json.JSONDecodeError as ex:
|
||||
raise PluginInitializationError("Parsing of Open AI manifest for auth config failed.") from ex
|
||||
openai_auth_config = OpenAIAuthenticationConfig(**plugin_json["auth"])
|
||||
openapi_spec_url = OpenAIUtils.parse_openai_manifest_for_openapi_spec_url(plugin_json=plugin_json)
|
||||
|
||||
# Modify the auth callback in execution parameters if it's provided
|
||||
if execution_parameters and execution_parameters.auth_callback:
|
||||
initial_auth_callback = execution_parameters.auth_callback
|
||||
|
||||
async def custom_auth_callback(**kwargs: Any):
|
||||
return await initial_auth_callback(plugin_name, openai_auth_config, **kwargs) # pragma: no cover
|
||||
|
||||
execution_parameters.auth_callback = custom_auth_callback
|
||||
|
||||
return cls(
|
||||
name=plugin_name,
|
||||
description=description,
|
||||
functions=create_functions_from_openapi(
|
||||
plugin_name=plugin_name,
|
||||
openapi_document_path=openapi_spec_url,
|
||||
execution_settings=execution_parameters,
|
||||
),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_python_file(
|
||||
cls,
|
||||
plugin_name: str,
|
||||
py_file: str,
|
||||
description: str | None = None,
|
||||
class_init_arguments: dict[str, dict[str, Any]] | None = None,
|
||||
) -> "KernelPlugin":
|
||||
module_name = os.path.basename(py_file).replace(".py", "")
|
||||
spec = importlib.util.spec_from_file_location(module_name, py_file)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
assert spec.loader
|
||||
spec.loader.exec_module(module)
|
||||
|
||||
for name, cls_instance in inspect.getmembers(module, inspect.isclass):
|
||||
if cls_instance.__module__ != module_name:
|
||||
continue
|
||||
instance = getattr(module, name)(**class_init_arguments.get(name, {}) if class_init_arguments else {})
|
||||
return cls.from_object(plugin_name=plugin_name, description=description, plugin_instance=instance)
|
||||
raise PluginInitializationError(f"No class found in file: {py_file}")
|
||||
|
||||
# endregion
|
||||
# region Internal Static Methods
|
||||
|
||||
@staticmethod
|
||||
def _validate_functions(
|
||||
functions: (
|
||||
KERNEL_FUNCTION_TYPE
|
||||
| list[KERNEL_FUNCTION_TYPE | KernelPlugin]
|
||||
| dict[str, KERNEL_FUNCTION_TYPE]
|
||||
| KernelPlugin
|
||||
| None
|
||||
),
|
||||
plugin_name: str,
|
||||
) -> dict[str, KernelFunction]:
|
||||
"""Validates the functions and returns a dictionary of functions."""
|
||||
if not functions or not plugin_name:
|
||||
# if the plugin_name is not present, the validation will fail, so no point in parsing.
|
||||
return {}
|
||||
if isinstance(functions, dict):
|
||||
return {
|
||||
name: KernelPlugin._parse_or_copy(function=function, plugin_name=plugin_name)
|
||||
for name, function in functions.items()
|
||||
}
|
||||
if isinstance(functions, KernelPlugin):
|
||||
return {
|
||||
name: function.function_copy(plugin_name=plugin_name) for name, function in functions.functions.items()
|
||||
}
|
||||
if isinstance(functions, KernelFunction):
|
||||
return {functions.name: KernelPlugin._parse_or_copy(function=functions, plugin_name=plugin_name)}
|
||||
if isinstance(functions, Callable):
|
||||
function = KernelPlugin._parse_or_copy(function=functions, plugin_name=plugin_name)
|
||||
return {function.name: function}
|
||||
if isinstance(functions, list):
|
||||
functions_dict: dict[str, KernelFunction] = {}
|
||||
for function in functions:
|
||||
if isinstance(function, (KernelFunction, Callable)):
|
||||
function = KernelPlugin._parse_or_copy(function=function, plugin_name=plugin_name)
|
||||
functions_dict[function.name] = function
|
||||
elif isinstance(function, KernelPlugin): # type: ignore
|
||||
functions_dict.update(
|
||||
{
|
||||
name: KernelPlugin._parse_or_copy(function=function, plugin_name=plugin_name)
|
||||
for name, function in function.functions.items()
|
||||
}
|
||||
)
|
||||
else:
|
||||
raise ValueError(f"Invalid type for functions in list: {function} (type: {type(function)})")
|
||||
return functions_dict
|
||||
raise ValueError(f"Invalid type for supplied functions: {functions} (type: {type(functions)})")
|
||||
|
||||
@staticmethod
|
||||
def _parse_or_copy(function: KERNEL_FUNCTION_TYPE, plugin_name: str) -> KernelFunction:
|
||||
"""Handle the function and return a KernelFunction instance."""
|
||||
if isinstance(function, KernelFunction):
|
||||
return function.function_copy(plugin_name=plugin_name)
|
||||
if isinstance(function, Callable):
|
||||
return KernelFunctionFromMethod(method=function, plugin_name=plugin_name)
|
||||
raise ValueError(f"Invalid type for function: {function} (type: {type(function)})")
|
||||
|
||||
# endregion
|
||||
|
||||
@@ -1,231 +0,0 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Any, Dict, Iterable, List, TypeVar, Union
|
||||
|
||||
from semantic_kernel.exceptions import (
|
||||
PluginInitializationError,
|
||||
PluginInvalidNameError,
|
||||
)
|
||||
from semantic_kernel.functions.kernel_function_metadata import KernelFunctionMetadata
|
||||
from semantic_kernel.functions.kernel_plugin import KernelPlugin
|
||||
from semantic_kernel.kernel_pydantic import KernelBaseModel
|
||||
|
||||
# To support Python 3.8, need to use TypeVar since Iterable is not scriptable
|
||||
KernelPluginType = TypeVar("KernelPluginType", bound=KernelPlugin)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from semantic_kernel.functions.kernel_function import KernelFunction
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class KernelPluginCollection(KernelBaseModel):
|
||||
"""
|
||||
The Kernel Plugin Collection class. This class is used to store a collection of plugins.
|
||||
|
||||
Attributes:
|
||||
plugins (Dict[str, KernelPlugin]): The plugins in the collection, indexed by their name.
|
||||
"""
|
||||
|
||||
plugins: Dict[str, "KernelPlugin"]
|
||||
|
||||
def __init__(self, plugins: Union[None, "KernelPluginCollection", Iterable[KernelPluginType]] = None):
|
||||
"""
|
||||
Initialize a new instance of the KernelPluginCollection class
|
||||
|
||||
Args:
|
||||
plugins (Union[None, KernelPluginCollection, Iterable[KernelPlugin]]): The plugins to add
|
||||
to the collection. If None, an empty collection is created. If a KernelPluginCollection,
|
||||
the plugins are copied from the other collection. If an iterable of KernelPlugin,
|
||||
the plugins are added to the collection.
|
||||
|
||||
Raises:
|
||||
ValueError: If the plugins is not None, a KernelPluginCollection, or an iterable of KernelPlugin.
|
||||
"""
|
||||
if plugins is None:
|
||||
plugins = {}
|
||||
elif isinstance(plugins, KernelPluginCollection):
|
||||
# Extract plugins from another KernelPluginCollection instance
|
||||
plugins = {plugin.name: plugin for plugin in plugins.plugins.values()}
|
||||
elif isinstance(plugins, Iterable):
|
||||
# Process an iterable of plugins
|
||||
plugins = self._process_plugins_iterable(plugins)
|
||||
else:
|
||||
raise PluginInitializationError("Invalid type for plugins")
|
||||
|
||||
super().__init__(plugins=plugins)
|
||||
|
||||
@staticmethod
|
||||
def _process_plugins_iterable(plugins_input: Iterable[KernelPlugin]) -> Dict[str, "KernelPlugin"]:
|
||||
plugins_dict = {}
|
||||
for plugin in plugins_input:
|
||||
if plugin is None:
|
||||
raise PluginInvalidNameError("Plugin and plugin.name must not be None")
|
||||
if plugin.name in plugins_dict:
|
||||
raise PluginInvalidNameError(f"Duplicate plugin name detected: {plugin.name}")
|
||||
plugins_dict[plugin.name] = plugin
|
||||
return plugins_dict
|
||||
|
||||
def add(self, plugin: "KernelPlugin") -> None:
|
||||
"""
|
||||
Add a single plugin to the collection
|
||||
|
||||
Args:
|
||||
plugin (KernelPlugin): The plugin to add to the collection.
|
||||
|
||||
Raises:
|
||||
ValueError: If the plugin or plugin.name is None.
|
||||
"""
|
||||
if plugin.name in self.plugins.keys():
|
||||
logger.warning(f'Overwriting plugin "{plugin.name}" in collection')
|
||||
self.plugins[plugin.name] = plugin
|
||||
|
||||
def add_plugin_from_functions(self, plugin_name: str, functions: List["KernelFunction"]) -> None:
|
||||
"""
|
||||
Add a function to a new plugin in the collection
|
||||
|
||||
Args:
|
||||
plugin_name (str): The name of the plugin to create.
|
||||
functions (List[KernelFunction]): The functions to add to the plugin.
|
||||
|
||||
Raises:
|
||||
ValueError: If the function or plugin_name is None or invalid.
|
||||
"""
|
||||
if not functions or not plugin_name:
|
||||
raise PluginInitializationError("Functions or plugin_name must not be None or empty")
|
||||
|
||||
plugin = KernelPlugin.from_functions(plugin_name=plugin_name, functions=functions)
|
||||
self.plugins[plugin_name] = plugin
|
||||
|
||||
def add_functions_to_plugin(self, functions: List["KernelFunction"], plugin_name: str) -> None:
|
||||
"""
|
||||
Add functions to a plugin in the collection
|
||||
|
||||
Args:
|
||||
functions (List[KernelFunction]): The function to add to the plugin.
|
||||
plugin_name (str): The name of the plugin to add the function to.
|
||||
|
||||
Raises:
|
||||
ValueError: If the functions or plugin_name is None or invalid.
|
||||
ValueError: if the function already exists in the plugin.
|
||||
"""
|
||||
if not functions or not plugin_name:
|
||||
raise PluginInitializationError("Functions and plugin_name must not be None or empty")
|
||||
|
||||
if plugin_name not in self.plugins:
|
||||
self.plugins[plugin_name] = KernelPlugin(name=plugin_name, functions=functions)
|
||||
return
|
||||
|
||||
for func in functions:
|
||||
if func.name in self.plugins[plugin_name].functions:
|
||||
logger.warning(f'Overwriting function "{func.name}" in collection')
|
||||
self.plugins[plugin_name].functions[func.name] = func
|
||||
|
||||
def add_list_of_plugins(self, plugins: List["KernelPlugin"]) -> None:
|
||||
"""
|
||||
Add a list of plugins to the collection
|
||||
|
||||
Args:
|
||||
plugins (List[KernelPlugin]): The plugins to add to the collection.
|
||||
|
||||
Raises:
|
||||
ValueError: If the plugins list is None.
|
||||
"""
|
||||
|
||||
if plugins is None:
|
||||
raise PluginInitializationError("Plugins must not be None")
|
||||
for plugin in plugins:
|
||||
self.add(plugin)
|
||||
|
||||
def remove(self, plugin: "KernelPlugin") -> bool:
|
||||
"""
|
||||
Remove a plugin from the collection
|
||||
|
||||
Args:
|
||||
plugin (KernelPlugin): The plugin to remove from the collection.
|
||||
|
||||
Returns:
|
||||
True if the plugin was removed, False otherwise.
|
||||
"""
|
||||
if plugin is None or plugin.name is None:
|
||||
return False
|
||||
return self.plugins.pop(plugin.name, None) is not None
|
||||
|
||||
def remove_by_name(self, plugin_name: str) -> bool:
|
||||
"""
|
||||
Remove a plugin from the collection by name
|
||||
|
||||
Args:
|
||||
plugin_name (str): The name of the plugin to remove from the collection.
|
||||
|
||||
Returns:
|
||||
True if the plugin was removed, False otherwise.
|
||||
"""
|
||||
if plugin_name is None:
|
||||
return False
|
||||
return self.plugins.pop(plugin_name, None) is not None
|
||||
|
||||
def __getitem__(self, name):
|
||||
"""Define the [] operator for the collection
|
||||
|
||||
Args:
|
||||
name (str): The name of the plugin to retrieve.
|
||||
|
||||
Returns:
|
||||
The plugin if it exists, None otherwise.
|
||||
|
||||
Raises:
|
||||
KeyError: If the plugin does not exist.
|
||||
"""
|
||||
if name not in self.plugins:
|
||||
raise KeyError(f"Plugin {name} not found.")
|
||||
return self.plugins[name]
|
||||
|
||||
def clear(self):
|
||||
"""Clear the collection of all plugins"""
|
||||
self.plugins.clear()
|
||||
|
||||
def get_list_of_function_metadata(
|
||||
self, include_prompt: bool = True, include_native: bool = True
|
||||
) -> List[KernelFunctionMetadata]:
|
||||
"""
|
||||
Get a list of the function metadata in the plugin collection
|
||||
|
||||
Args:
|
||||
include_prompt (bool): Whether to include semantic functions in the list.
|
||||
include_native (bool): Whether to include native functions in the list.
|
||||
|
||||
Returns:
|
||||
A list of KernelFunctionMetadata objects in the collection.
|
||||
"""
|
||||
if not self.plugins:
|
||||
return []
|
||||
return [
|
||||
func.metadata
|
||||
for plugin in self.plugins.values()
|
||||
for func in plugin.functions.values()
|
||||
if (include_prompt and func.is_prompt) or (include_native and not func.is_prompt)
|
||||
]
|
||||
|
||||
def __iter__(self) -> Any:
|
||||
"""Define an iterator for the collection"""
|
||||
return iter(self.plugins.values())
|
||||
|
||||
def __len__(self) -> int:
|
||||
"""Define the length of the collection"""
|
||||
return len(self.plugins)
|
||||
|
||||
def __contains__(self, plugin_name: str) -> bool:
|
||||
"""
|
||||
Check if the collection contains a plugin
|
||||
|
||||
Args:
|
||||
plugin_name (str): The name of the plugin to check for.
|
||||
|
||||
Returns:
|
||||
True if the collection contains the plugin, False otherwise.
|
||||
"""
|
||||
if not plugin_name:
|
||||
return False
|
||||
return self.plugins.get(plugin_name) is not None
|
||||
@@ -0,0 +1,7 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
from typing import Any, Callable, Union
|
||||
|
||||
from semantic_kernel.functions.kernel_function import KernelFunction
|
||||
|
||||
KERNEL_FUNCTION_TYPE = Union[KernelFunction, Callable[..., Any]]
|
||||
+301
-436
@@ -1,55 +1,28 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
from __future__ import annotations
|
||||
|
||||
import glob
|
||||
import importlib
|
||||
import inspect
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from copy import copy
|
||||
from types import MethodType
|
||||
from typing import TYPE_CHECKING, Any, AsyncIterable, Callable, ItemsView, Literal, Type, TypeVar, Union
|
||||
from typing import TYPE_CHECKING, Any, AsyncIterable, Callable, Literal, Type, TypeVar, Union
|
||||
|
||||
import httpx
|
||||
import yaml
|
||||
from pydantic import Field, field_validator
|
||||
|
||||
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
|
||||
from semantic_kernel.connectors.openai_plugin.openai_authentication_config import OpenAIAuthenticationConfig
|
||||
from semantic_kernel.connectors.openai_plugin.openai_function_execution_parameters import (
|
||||
OpenAIFunctionExecutionParameters,
|
||||
)
|
||||
from semantic_kernel.connectors.openai_plugin.openai_utils import OpenAIUtils
|
||||
from semantic_kernel.connectors.openapi_plugin.openapi_function_execution_parameters import (
|
||||
OpenAPIFunctionExecutionParameters,
|
||||
)
|
||||
from semantic_kernel.connectors.openapi_plugin.openapi_manager import OpenAPIPlugin
|
||||
from semantic_kernel.connectors.utils.document_loader import DocumentLoader
|
||||
from semantic_kernel.contents.streaming_content_mixin import StreamingContentMixin
|
||||
from semantic_kernel.events import FunctionInvokedEventArgs, FunctionInvokingEventArgs
|
||||
from semantic_kernel.exceptions import (
|
||||
FunctionInitializationError,
|
||||
FunctionNameNotUniqueError,
|
||||
KernelFunctionAlreadyExistsError,
|
||||
KernelFunctionNotFoundError,
|
||||
KernelInvokeException,
|
||||
KernelPluginInvalidConfigurationError,
|
||||
KernelPluginNotFoundError,
|
||||
KernelServiceNotFoundError,
|
||||
PluginInitializationError,
|
||||
PluginInvalidNameError,
|
||||
ServiceInvalidTypeError,
|
||||
TemplateSyntaxError,
|
||||
)
|
||||
from semantic_kernel.functions.function_result import FunctionResult
|
||||
from semantic_kernel.functions.kernel_arguments import KernelArguments
|
||||
from semantic_kernel.functions.kernel_function import TEMPLATE_FORMAT_MAP, KernelFunction
|
||||
from semantic_kernel.functions.kernel_function_from_method import KernelFunctionFromMethod
|
||||
from semantic_kernel.functions.kernel_function_from_prompt import KernelFunctionFromPrompt
|
||||
from semantic_kernel.functions.kernel_function_metadata import KernelFunctionMetadata
|
||||
from semantic_kernel.functions.kernel_plugin import KernelPlugin
|
||||
from semantic_kernel.functions.kernel_plugin_collection import KernelPluginCollection
|
||||
from semantic_kernel.kernel_pydantic import KernelBaseModel
|
||||
from semantic_kernel.prompt_template.const import KERNEL_TEMPLATE_FORMAT_NAME, TEMPLATE_FORMAT_TYPES
|
||||
from semantic_kernel.prompt_template.prompt_template_base import PromptTemplateBase
|
||||
@@ -58,12 +31,19 @@ from semantic_kernel.reliability.pass_through_without_retry import PassThroughWi
|
||||
from semantic_kernel.reliability.retry_mechanism_base import RetryMechanismBase
|
||||
from semantic_kernel.services.ai_service_client_base import AIServiceClientBase
|
||||
from semantic_kernel.services.ai_service_selector import AIServiceSelector
|
||||
from semantic_kernel.utils.validation import validate_plugin_name
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from semantic_kernel.connectors.ai.chat_completion_client_base import ChatCompletionClientBase
|
||||
from semantic_kernel.connectors.ai.embeddings.embedding_generator_base import EmbeddingGeneratorBase
|
||||
from semantic_kernel.connectors.ai.text_completion_client_base import TextCompletionClientBase
|
||||
from semantic_kernel.connectors.openai_plugin.openai_function_execution_parameters import (
|
||||
OpenAIFunctionExecutionParameters,
|
||||
)
|
||||
from semantic_kernel.connectors.openapi_plugin.openapi_function_execution_parameters import (
|
||||
OpenAPIFunctionExecutionParameters,
|
||||
)
|
||||
from semantic_kernel.functions.kernel_function import KernelFunction
|
||||
from semantic_kernel.functions.types import KERNEL_FUNCTION_TYPE
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
@@ -78,7 +58,7 @@ class Kernel(KernelBaseModel):
|
||||
semantic/native functions, and manage plugins, memory, and AI services.
|
||||
|
||||
Attributes:
|
||||
plugins (KernelPluginCollection | None): The collection of plugins to be used by the kernel
|
||||
plugins (dict[str, KernelPlugin] | None): The plugins to be used by the kernel
|
||||
services (dict[str, AIServiceClientBase]): The services to be used by the kernel
|
||||
retry_mechanism (RetryMechanismBase): The retry mechanism to be used by the kernel
|
||||
function_invoking_handlers (dict): The function invoking handlers
|
||||
@@ -87,7 +67,7 @@ class Kernel(KernelBaseModel):
|
||||
|
||||
# region Init
|
||||
|
||||
plugins: KernelPluginCollection = Field(default_factory=KernelPluginCollection)
|
||||
plugins: dict[str, KernelPlugin] = Field(default_factory=dict)
|
||||
services: dict[str, AIServiceClientBase] = Field(default_factory=dict)
|
||||
ai_service_selector: AIServiceSelector = Field(default_factory=AIServiceSelector)
|
||||
retry_mechanism: RetryMechanismBase = Field(default_factory=PassThroughWithoutRetry)
|
||||
@@ -100,7 +80,7 @@ class Kernel(KernelBaseModel):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
plugins: KernelPluginCollection | None = None,
|
||||
plugins: KernelPlugin | dict[str, KernelPlugin] | list[KernelPlugin] | None = None,
|
||||
services: AIServiceClientBase | list[AIServiceClientBase] | dict[str, AIServiceClientBase] | None = None,
|
||||
ai_service_selector: AIServiceSelector | None = None,
|
||||
**kwargs: Any,
|
||||
@@ -109,10 +89,10 @@ class Kernel(KernelBaseModel):
|
||||
Initialize a new instance of the Kernel class.
|
||||
|
||||
Args:
|
||||
plugins (KernelPluginCollection | None): The collection of plugins to be used by the kernel
|
||||
plugins (KernelPlugin | dict[str, KernelPlugin] | list[KernelPlugin] | None):
|
||||
The plugins to be used by the kernel, will be rewritten to a dict with plugin name as key
|
||||
services (AIServiceClientBase | list[AIServiceClientBase] | dict[str, AIServiceClientBase] | None:
|
||||
The services to be used by the kernel,
|
||||
will be rewritten to a dict with service_id as key
|
||||
The services to be used by the kernel, will be rewritten to a dict with service_id as key
|
||||
ai_service_selector (AIServiceSelector | None): The AI service selector to be used by the kernel,
|
||||
default is based on order of execution settings.
|
||||
**kwargs (Any): Additional fields to be passed to the Kernel model,
|
||||
@@ -123,14 +103,27 @@ class Kernel(KernelBaseModel):
|
||||
"""
|
||||
args = {
|
||||
"services": services,
|
||||
"plugins": plugins,
|
||||
**kwargs,
|
||||
}
|
||||
if ai_service_selector:
|
||||
args["ai_service_selector"] = ai_service_selector
|
||||
if plugins:
|
||||
args["plugins"] = plugins
|
||||
super().__init__(**args)
|
||||
|
||||
@field_validator("plugins", mode="before")
|
||||
@classmethod
|
||||
def rewrite_plugins(
|
||||
cls, plugins: KernelPlugin | list[KernelPlugin] | dict[str, KernelPlugin] | None = None
|
||||
) -> dict[str, KernelPlugin]:
|
||||
"""Rewrite plugins to a dictionary."""
|
||||
if not plugins:
|
||||
return {}
|
||||
if isinstance(plugins, KernelPlugin):
|
||||
return {plugins.name: plugins}
|
||||
if isinstance(plugins, list):
|
||||
return {p.name: p for p in plugins}
|
||||
return plugins
|
||||
|
||||
@field_validator("services", mode="before")
|
||||
@classmethod
|
||||
def rewrite_services(
|
||||
@@ -151,7 +144,7 @@ class Kernel(KernelBaseModel):
|
||||
|
||||
async def invoke_stream(
|
||||
self,
|
||||
function: KernelFunction | None = None,
|
||||
function: "KernelFunction" | None = None,
|
||||
arguments: KernelArguments | None = None,
|
||||
function_name: str | None = None,
|
||||
plugin_name: str | None = None,
|
||||
@@ -182,7 +175,7 @@ class Kernel(KernelBaseModel):
|
||||
if not function:
|
||||
if not function_name or not plugin_name:
|
||||
raise KernelFunctionNotFoundError("No function(s) or function- and plugin-name provided")
|
||||
function = self.func(plugin_name, function_name)
|
||||
function = self.get_function(plugin_name, function_name)
|
||||
|
||||
function_invoking_args = self.on_function_invoking(function.metadata, arguments)
|
||||
if function_invoking_args.is_cancel_requested:
|
||||
@@ -227,7 +220,7 @@ class Kernel(KernelBaseModel):
|
||||
|
||||
async def invoke(
|
||||
self,
|
||||
function: KernelFunction | None = None,
|
||||
function: "KernelFunction" | None = None,
|
||||
arguments: KernelArguments | None = None,
|
||||
function_name: str | None = None,
|
||||
plugin_name: str | None = None,
|
||||
@@ -255,7 +248,7 @@ class Kernel(KernelBaseModel):
|
||||
if not function:
|
||||
if not function_name or not plugin_name:
|
||||
raise KernelFunctionNotFoundError("No function or plugin name provided")
|
||||
function = self.func(plugin_name, function_name)
|
||||
function = self.get_function(plugin_name, function_name)
|
||||
function_invoking_args = self.on_function_invoking(function.metadata, arguments)
|
||||
if function_invoking_args.is_cancel_requested:
|
||||
logger.info(
|
||||
@@ -343,6 +336,8 @@ class Kernel(KernelBaseModel):
|
||||
if not prompt:
|
||||
raise TemplateSyntaxError("The prompt is either null or empty.")
|
||||
|
||||
from semantic_kernel.functions.kernel_function_from_prompt import KernelFunctionFromPrompt
|
||||
|
||||
function = KernelFunctionFromPrompt(
|
||||
function_name=function_name,
|
||||
plugin_name=plugin_name,
|
||||
@@ -401,201 +396,175 @@ class Kernel(KernelBaseModel):
|
||||
del self.function_invoked_handlers[id(handler)]
|
||||
|
||||
# endregion
|
||||
# region Plugins
|
||||
# region Plugins & Functions
|
||||
|
||||
def add_plugin(self, plugin_name: str, functions: list[KernelFunction], plugin: KernelPlugin | None = None) -> None:
|
||||
def add_plugin(
|
||||
self,
|
||||
plugin: KernelPlugin | Any | dict[str, Any] | None = None,
|
||||
plugin_name: str | None = None,
|
||||
parent_directory: str | None = None,
|
||||
description: str | None = None,
|
||||
class_init_arguments: dict[str, dict[str, Any]] | None = None,
|
||||
) -> "KernelPlugin":
|
||||
"""
|
||||
Adds a plugin to the kernel's collection of plugins. If a plugin instance is provided,
|
||||
Adds a plugin to the kernel's collection of plugins. If a plugin is provided,
|
||||
it uses that instance instead of creating a new KernelPlugin.
|
||||
See KernelPlugin.from_directory for more details on how the directory is parsed.
|
||||
|
||||
Args:
|
||||
plugin_name (str): The name of the plugin
|
||||
functions (list[KernelFunction]): The functions to add to the plugin
|
||||
plugin (KernelPlugin | None): An optional pre-defined plugin instance
|
||||
"""
|
||||
if plugin is None:
|
||||
# If no plugin instance is provided, create a new KernelPlugin
|
||||
plugin = KernelPlugin(name=plugin_name, functions=functions)
|
||||
|
||||
if plugin_name in self.plugins:
|
||||
self.plugins.add_functions_to_plugin(functions=functions, plugin_name=plugin_name)
|
||||
else:
|
||||
self.plugins.add(plugin)
|
||||
|
||||
def import_plugin_from_object(self, plugin_instance: Any | dict[str, Any], plugin_name: str) -> KernelPlugin:
|
||||
"""
|
||||
Creates a plugin that wraps the specified target object and imports it into the kernel's plugin collection
|
||||
|
||||
Args:
|
||||
plugin_instance (Any | dict[str, Any]): The plugin instance. This can be a custom class or a
|
||||
dictionary of classes that contains methods with the kernel_function decorator for one or
|
||||
several methods. See `TextMemoryPlugin` as an example.
|
||||
plugin_name (str): The name of the plugin. Allows chars: upper, lower ASCII and underscores.
|
||||
plugin (KernelPlugin | Any | dict[str, Any]): The plugin to add.
|
||||
This can be a KernelPlugin, in which case it is added straightaway and other parameters are ignored,
|
||||
a custom class that contains methods with the kernel_function decorator
|
||||
or a dictionary of functions with the kernel_function decorator for one or
|
||||
several methods.
|
||||
plugin_name (str | None): The name of the plugin, used if the plugin is not a KernelPlugin,
|
||||
if the plugin is None and the parent_directory is set,
|
||||
KernelPlugin.from_directory is called with those parameters,
|
||||
see `KernelPlugin.from_directory` for details.
|
||||
parent_directory (str | None): The parent directory path where the plugin directory resides
|
||||
description (str | None): The description of the plugin, used if the plugin is not a KernelPlugin.
|
||||
class_init_arguments (dict[str, dict[str, Any]] | None): The class initialization arguments
|
||||
|
||||
Returns:
|
||||
KernelPlugin: The imported plugin of type KernelPlugin.
|
||||
"""
|
||||
if not plugin_name.strip():
|
||||
raise PluginInvalidNameError("Plugin name cannot be empty")
|
||||
logger.debug(f"Importing plugin {plugin_name}")
|
||||
|
||||
functions: dict[str, KernelFunction] = {}
|
||||
candidates: list[tuple[str, MethodType]] | ItemsView[str, Any] = []
|
||||
|
||||
if isinstance(plugin_instance, dict):
|
||||
candidates = plugin_instance.items()
|
||||
else:
|
||||
candidates = inspect.getmembers(plugin_instance, inspect.ismethod)
|
||||
# Read every method from the plugin instance
|
||||
for _, candidate in candidates:
|
||||
# If the method is a prompt function, register it
|
||||
if not hasattr(candidate, "__kernel_function__"):
|
||||
continue
|
||||
|
||||
func = KernelFunctionFromMethod(plugin_name=plugin_name, method=candidate)
|
||||
if func.name in functions:
|
||||
raise FunctionNameNotUniqueError(
|
||||
"Overloaded functions are not supported, " "please differentiate function names."
|
||||
)
|
||||
functions[func.name] = func
|
||||
logger.debug(f"Methods imported: {len(functions)}")
|
||||
|
||||
plugin = KernelPlugin(name=plugin_name, functions=functions)
|
||||
self.plugins.add(plugin)
|
||||
|
||||
return plugin
|
||||
|
||||
def import_native_plugin_from_directory(
|
||||
self, parent_directory: str, plugin_directory_name: str
|
||||
) -> KernelPlugin | None:
|
||||
MODULE_NAME = "native_function"
|
||||
|
||||
validate_plugin_name(plugin_directory_name)
|
||||
|
||||
plugin_directory = os.path.abspath(os.path.join(parent_directory, plugin_directory_name))
|
||||
native_py_file_path = os.path.join(plugin_directory, f"{MODULE_NAME}.py")
|
||||
|
||||
if not os.path.exists(native_py_file_path):
|
||||
raise PluginInitializationError(f"Native Plugin Python File does not exist: {native_py_file_path}")
|
||||
|
||||
plugin_name = os.path.basename(plugin_directory)
|
||||
|
||||
spec = importlib.util.spec_from_file_location(MODULE_NAME, native_py_file_path)
|
||||
if not spec:
|
||||
raise PluginInitializationError(f"Failed to load plugin: {plugin_name}")
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
assert spec.loader
|
||||
spec.loader.exec_module(module)
|
||||
|
||||
class_name = next(
|
||||
(name for name, cls in inspect.getmembers(module, inspect.isclass) if cls.__module__ == MODULE_NAME),
|
||||
None,
|
||||
)
|
||||
if class_name:
|
||||
plugin_obj = getattr(module, class_name)()
|
||||
return self.import_plugin_from_object(plugin_obj, plugin_name)
|
||||
|
||||
return None
|
||||
|
||||
def import_plugin_from_prompt_directory(self, parent_directory: str, plugin_directory_name: str) -> KernelPlugin:
|
||||
"""
|
||||
Import a plugin from a specified directory, processing both YAML files and subdirectories
|
||||
containing `skprompt.txt` and `config.json` files to create KernelFunction objects. These objects
|
||||
are then grouped into a single KernelPlugin instance.
|
||||
|
||||
This method does not recurse into subdirectories beyond one level deep from the specified plugin directory.
|
||||
For YAML files, function names are extracted from the content of the YAML files themselves (the name property).
|
||||
For directories, the function name is assumed to be the name of the directory. Each KernelFunction object is
|
||||
initialized with data parsed from the associated files and added to a list of functions that are then assigned
|
||||
to the created KernelPlugin object.
|
||||
|
||||
Args:
|
||||
parent_directory (str): The parent directory path where the plugin directory resides. This should be
|
||||
an absolute path to ensure correct file resolution.
|
||||
plugin_directory_name (str): The name of the directory that contains the plugin's YAML files and
|
||||
subdirectories. This directory name is used as the plugin name and should be directly under the
|
||||
parent_directory.
|
||||
|
||||
Returns:
|
||||
KernelPlugin: An instance of KernelPlugin containing all the KernelFunction objects created from
|
||||
the YAML files and directories found in the specified plugin directory. The name of the
|
||||
plugin is set to the plugin_directory_name.
|
||||
KernelPlugin: The plugin that was added.
|
||||
|
||||
Raises:
|
||||
PluginInitializationError: If the plugin directory does not exist.
|
||||
PluginInvalidNameError: If the plugin name is invalid.
|
||||
ValidationError: If a KernelPlugin needs to be created, but it is not valid.
|
||||
|
||||
Example:
|
||||
Assuming a plugin directory structure as follows:
|
||||
|
||||
MyPlugins/
|
||||
|--- pluginA.yaml
|
||||
|--- pluginB.yaml
|
||||
|--- Directory1/
|
||||
|--- skprompt.txt
|
||||
|--- config.json
|
||||
|--- Directory2/
|
||||
|--- skprompt.txt
|
||||
|--- config.json
|
||||
|
||||
Calling `import_plugin("/path/to", "MyPlugins")` will create a KernelPlugin object named
|
||||
"MyPlugins", containing KernelFunction objects for `pluginA.yaml`, `pluginB.yaml`,
|
||||
`Directory1`, and `Directory2`, each initialized with their respective configurations.
|
||||
"""
|
||||
plugin_directory = self._validate_plugin_directory(
|
||||
parent_directory=parent_directory, plugin_directory_name=plugin_directory_name
|
||||
)
|
||||
if isinstance(plugin, KernelPlugin):
|
||||
self.plugins[plugin.name] = plugin
|
||||
return self.plugins[plugin.name]
|
||||
if not plugin_name:
|
||||
raise ValueError("plugin_name must be provided if a plugin is not supplied.")
|
||||
if plugin:
|
||||
self.plugins[plugin_name] = KernelPlugin.from_object(
|
||||
plugin_name=plugin_name, plugin_instance=plugin, description=description
|
||||
)
|
||||
return self.plugins[plugin_name]
|
||||
if plugin is None and parent_directory is not None:
|
||||
self.plugins[plugin_name] = KernelPlugin.from_directory(
|
||||
plugin_name=plugin_name,
|
||||
parent_directory=parent_directory,
|
||||
description=description,
|
||||
class_init_arguments=class_init_arguments,
|
||||
)
|
||||
return self.plugins[plugin_name]
|
||||
raise ValueError("plugin or parent_directory must be provided.")
|
||||
|
||||
functions = []
|
||||
def add_plugins(self, plugins: list[KernelPlugin | object] | dict[str, KernelPlugin | object]) -> None:
|
||||
"""
|
||||
Adds a list of plugins to the kernel's collection of plugins.
|
||||
|
||||
# Handle YAML files at the root
|
||||
yaml_files = glob.glob(os.path.join(plugin_directory, "*.yaml"))
|
||||
for yaml_file in yaml_files:
|
||||
with open(yaml_file, "r") as file:
|
||||
yaml_content = file.read()
|
||||
functions.append(self.create_function_from_yaml(yaml_content, plugin_name=plugin_directory_name))
|
||||
Args:
|
||||
plugins (list[KernelPlugin] | dict[str, KernelPlugin]): The plugins to add to the kernel
|
||||
"""
|
||||
if isinstance(plugins, list):
|
||||
for plugin in plugins:
|
||||
self.add_plugin(plugin)
|
||||
return
|
||||
for name, plugin in plugins.items():
|
||||
self.add_plugin(plugin, plugin_name=name)
|
||||
|
||||
# Handle directories containing skprompt.txt and config.json
|
||||
for item in os.listdir(plugin_directory):
|
||||
item_path = os.path.join(plugin_directory, item)
|
||||
if os.path.isdir(item_path):
|
||||
prompt_path = os.path.join(item_path, "skprompt.txt")
|
||||
config_path = os.path.join(item_path, "config.json")
|
||||
|
||||
if os.path.exists(prompt_path) and os.path.exists(config_path):
|
||||
with open(config_path, "r") as config_file:
|
||||
prompt_template_config = PromptTemplateConfig.from_json(config_file.read())
|
||||
prompt_template_config.name = item
|
||||
|
||||
with open(prompt_path, "r") as prompt_file:
|
||||
prompt = prompt_file.read()
|
||||
prompt_template_config.template = prompt
|
||||
|
||||
prompt_template = TEMPLATE_FORMAT_MAP[prompt_template_config.template_format]( # type: ignore
|
||||
prompt_template_config=prompt_template_config
|
||||
)
|
||||
|
||||
functions.append(
|
||||
self.create_function_from_prompt(
|
||||
plugin_name=plugin_directory_name,
|
||||
prompt_template=prompt_template,
|
||||
prompt_template_config=prompt_template_config,
|
||||
template_format=prompt_template_config.template_format,
|
||||
function_name=item,
|
||||
description=prompt_template_config.description,
|
||||
)
|
||||
)
|
||||
|
||||
return KernelPlugin(name=plugin_directory_name, functions=functions)
|
||||
|
||||
async def import_plugin_from_openai(
|
||||
def add_function(
|
||||
self,
|
||||
plugin_name: str,
|
||||
plugin_url: str | None = None,
|
||||
plugin_str: str | None = None,
|
||||
execution_parameters: OpenAIFunctionExecutionParameters | None = None,
|
||||
function: KERNEL_FUNCTION_TYPE | None = None,
|
||||
function_name: str | None = None,
|
||||
description: str | None = None,
|
||||
prompt: str | None = None,
|
||||
prompt_template_config: PromptTemplateConfig | None = None,
|
||||
prompt_execution_settings: (
|
||||
PromptExecutionSettings | list[PromptExecutionSettings] | dict[str, PromptExecutionSettings] | None
|
||||
) = None,
|
||||
template_format: TEMPLATE_FORMAT_TYPES = KERNEL_TEMPLATE_FORMAT_NAME,
|
||||
prompt_template: PromptTemplateBase | None = None,
|
||||
return_plugin: bool = False,
|
||||
**kwargs: Any,
|
||||
) -> "KernelFunction | KernelPlugin":
|
||||
"""
|
||||
Adds a function to the specified plugin.
|
||||
|
||||
Args:
|
||||
plugin_name (str): The name of the plugin to add the function to
|
||||
function (KernelFunction | Callable[..., Any]): The function to add
|
||||
function_name (str): The name of the function
|
||||
plugin_name (str): The name of the plugin
|
||||
description (str | None): The description of the function
|
||||
prompt (str | None): The prompt template.
|
||||
prompt_template_config (PromptTemplateConfig | None): The prompt template configuration
|
||||
prompt_execution_settings (PromptExecutionSettings | list[PromptExecutionSettings]
|
||||
| dict[str, PromptExecutionSettings] | None):
|
||||
The execution settings, will be parsed into a dict.
|
||||
template_format (str | None): The format of the prompt template
|
||||
prompt_template (PromptTemplateBase | None): The prompt template
|
||||
return_plugin (bool): If True, the plugin is returned instead of the function
|
||||
kwargs (Any): Additional arguments
|
||||
|
||||
Returns:
|
||||
KernelFunction | KernelPlugin: The function that was added, or the plugin if return_plugin is True
|
||||
|
||||
"""
|
||||
from semantic_kernel.functions.kernel_function import KernelFunction
|
||||
|
||||
if function is None:
|
||||
if not function_name or (not prompt and not prompt_template_config and not prompt_template):
|
||||
raise ValueError(
|
||||
"function_name and prompt, prompt_template_config or prompt_template must be provided if a function is not supplied." # noqa: E501
|
||||
)
|
||||
if prompt_execution_settings is None and (
|
||||
prompt_template_config is None or prompt_template_config.execution_settings is None
|
||||
):
|
||||
prompt_execution_settings = PromptExecutionSettings(extension_data=kwargs)
|
||||
|
||||
function = KernelFunction.from_prompt(
|
||||
function_name=function_name,
|
||||
plugin_name=plugin_name,
|
||||
description=description,
|
||||
prompt=prompt,
|
||||
template_format=template_format,
|
||||
prompt_template=prompt_template,
|
||||
prompt_template_config=prompt_template_config,
|
||||
prompt_execution_settings=prompt_execution_settings,
|
||||
)
|
||||
elif not isinstance(function, KernelFunction):
|
||||
function = KernelFunction.from_method(plugin_name=plugin_name, method=function)
|
||||
if plugin_name not in self.plugins:
|
||||
plugin = KernelPlugin(name=plugin_name, functions=function)
|
||||
self.add_plugin(plugin)
|
||||
return plugin if return_plugin else plugin[function.name]
|
||||
self.plugins[plugin_name][function.name] = function
|
||||
return self.plugins[plugin_name] if return_plugin else self.plugins[plugin_name][function.name]
|
||||
|
||||
def add_functions(
|
||||
self,
|
||||
plugin_name: str,
|
||||
functions: list[KERNEL_FUNCTION_TYPE] | dict[str, KERNEL_FUNCTION_TYPE],
|
||||
) -> "KernelPlugin":
|
||||
"""
|
||||
Adds a list of functions to the specified plugin.
|
||||
|
||||
Args:
|
||||
plugin_name (str): The name of the plugin to add the functions to
|
||||
functions (list[KernelFunction] | dict[str, KernelFunction]): The functions to add
|
||||
|
||||
Returns:
|
||||
KernelPlugin: The plugin that the functions were added to.
|
||||
|
||||
"""
|
||||
if plugin_name in self.plugins:
|
||||
self.plugins[plugin_name].update(functions)
|
||||
return self.plugins[plugin_name]
|
||||
return self.add_plugin(KernelPlugin(name=plugin_name, functions=functions)) # type: ignore
|
||||
|
||||
def add_plugin_from_openapi(
|
||||
self,
|
||||
plugin_name: str,
|
||||
openapi_document_path: str,
|
||||
execution_settings: "OpenAPIFunctionExecutionParameters | None" = None,
|
||||
description: str | None = None,
|
||||
) -> KernelPlugin:
|
||||
"""Create a plugin from the Open AI manifest.
|
||||
"""Add a plugin from the Open AI manifest.
|
||||
|
||||
Args:
|
||||
plugin_name (str): The name of the plugin
|
||||
@@ -609,246 +578,142 @@ class Kernel(KernelBaseModel):
|
||||
Raises:
|
||||
PluginInitializationError: if the plugin URL or plugin JSON/YAML is not provided
|
||||
"""
|
||||
|
||||
if execution_parameters is None:
|
||||
execution_parameters = OpenAIFunctionExecutionParameters()
|
||||
|
||||
validate_plugin_name(plugin_name)
|
||||
|
||||
if plugin_str is not None:
|
||||
# Load plugin from the provided JSON string/YAML string
|
||||
openai_manifest = plugin_str
|
||||
elif plugin_url is not None:
|
||||
# Load plugin from the URL
|
||||
http_client = execution_parameters.http_client if execution_parameters.http_client else httpx.AsyncClient()
|
||||
openai_manifest = await DocumentLoader.from_uri(
|
||||
url=plugin_url, http_client=http_client, auth_callback=None, user_agent=execution_parameters.user_agent
|
||||
return self.add_plugin(
|
||||
KernelPlugin.from_openapi(
|
||||
plugin_name=plugin_name,
|
||||
openapi_document_path=openapi_document_path,
|
||||
execution_settings=execution_settings,
|
||||
description=description,
|
||||
)
|
||||
else:
|
||||
raise PluginInitializationError("Either plugin_url or plugin_json must be provided.")
|
||||
|
||||
try:
|
||||
plugin_json = json.loads(openai_manifest)
|
||||
openai_auth_config = OpenAIAuthenticationConfig(**plugin_json["auth"])
|
||||
except json.JSONDecodeError as ex:
|
||||
raise KernelPluginInvalidConfigurationError("Parsing of Open AI manifest for auth config failed.") from ex
|
||||
|
||||
# Modify the auth callback in execution parameters if it's provided
|
||||
if execution_parameters and execution_parameters.auth_callback:
|
||||
initial_auth_callback = execution_parameters.auth_callback
|
||||
|
||||
async def custom_auth_callback(**kwargs):
|
||||
return await initial_auth_callback(plugin_name, openai_auth_config, **kwargs)
|
||||
|
||||
execution_parameters.auth_callback = custom_auth_callback
|
||||
|
||||
try:
|
||||
openapi_spec_url = OpenAIUtils.parse_openai_manifest_for_openapi_spec_url(plugin_json)
|
||||
except PluginInitializationError as ex:
|
||||
raise KernelPluginInvalidConfigurationError(
|
||||
"Parsing of Open AI manifest for OpenAPI spec URL failed."
|
||||
) from ex
|
||||
|
||||
return self.import_plugin_from_openapi(
|
||||
plugin_name=plugin_name,
|
||||
openapi_document_path=openapi_spec_url,
|
||||
execution_settings=execution_parameters,
|
||||
)
|
||||
|
||||
def import_plugin_from_openapi(
|
||||
async def add_plugin_from_openai(
|
||||
self,
|
||||
plugin_name: str,
|
||||
openapi_document_path: str,
|
||||
execution_settings: "OpenAIFunctionExecutionParameters" | "OpenAPIFunctionExecutionParameters" | None = None,
|
||||
plugin_url: str | None = None,
|
||||
plugin_str: str | None = None,
|
||||
execution_parameters: "OpenAIFunctionExecutionParameters | None" = None,
|
||||
description: str | None = None,
|
||||
) -> KernelPlugin:
|
||||
"""Create a plugin from an OpenAPI manifest.
|
||||
"""Add a plugin from an OpenAPI document.
|
||||
|
||||
Args:
|
||||
plugin_name (str): The name of the plugin
|
||||
openapi_document_path (str): The OpenAPI document path
|
||||
execution_settings (OpenAIFunctionExecutionParameters | OpenAPIFunctionExecutionParameters | None):
|
||||
The execution settings
|
||||
plugin_url (str | None): The URL of the plugin
|
||||
plugin_str (str | None): The JSON string of the plugin
|
||||
execution_parameters (OpenAIFunctionExecutionParameters | None): The execution parameters
|
||||
description (str | None): The description of the plugin
|
||||
|
||||
Returns:
|
||||
KernelPlugin: The imported plugin
|
||||
"""
|
||||
validate_plugin_name(plugin_name)
|
||||
|
||||
if not openapi_document_path:
|
||||
raise PluginInitializationError("OpenAPI document path is required.")
|
||||
|
||||
plugin = OpenAPIPlugin.create(
|
||||
plugin_name=plugin_name,
|
||||
openapi_document_path=openapi_document_path,
|
||||
execution_settings=execution_settings,
|
||||
)
|
||||
return self.import_plugin_from_object(plugin, plugin_name)
|
||||
|
||||
def _validate_plugin_directory(self, parent_directory: str, plugin_directory_name: str) -> str:
|
||||
"""Validate the plugin name and that the plugin directory exists.
|
||||
|
||||
Args:
|
||||
parent_directory (str): The parent directory
|
||||
plugin_directory_name (str): The plugin directory name
|
||||
|
||||
Returns:
|
||||
str: The plugin directory.
|
||||
|
||||
Raises:
|
||||
PluginInitializationError: If the plugin directory does not exist.
|
||||
PluginInvalidNameError: If the plugin name is invalid.
|
||||
PluginInitializationError: if the plugin URL or plugin JSON/YAML is not provided
|
||||
"""
|
||||
validate_plugin_name(plugin_directory_name)
|
||||
|
||||
plugin_directory = os.path.join(parent_directory, plugin_directory_name)
|
||||
plugin_directory = os.path.abspath(plugin_directory)
|
||||
|
||||
if not os.path.exists(plugin_directory):
|
||||
raise PluginInitializationError(f"Plugin directory does not exist: {plugin_directory_name}")
|
||||
|
||||
return plugin_directory
|
||||
|
||||
# endregion
|
||||
# region Functions
|
||||
|
||||
def func(self, plugin_name: str, function_name: str) -> KernelFunction:
|
||||
if plugin_name not in self.plugins:
|
||||
raise KernelPluginNotFoundError(f"Plugin '{plugin_name}' not found")
|
||||
if function_name not in self.plugins[plugin_name]:
|
||||
raise KernelFunctionNotFoundError(f"Function '{function_name}' not found in plugin '{plugin_name}'")
|
||||
return self.plugins[plugin_name][function_name]
|
||||
|
||||
def func_from_fully_qualified_function_name(self, fully_qualified_function_name: str) -> KernelFunction:
|
||||
plugin_name, function_name = fully_qualified_function_name.split("-")
|
||||
if plugin_name not in self.plugins:
|
||||
raise KernelPluginNotFoundError(f"Plugin '{plugin_name}' not found")
|
||||
if function_name not in self.plugins[plugin_name]:
|
||||
raise KernelFunctionNotFoundError(f"Function '{function_name}' not found in plugin '{plugin_name}'")
|
||||
return self.plugins[plugin_name][function_name]
|
||||
|
||||
def create_function_from_prompt(
|
||||
self,
|
||||
function_name: str,
|
||||
plugin_name: str,
|
||||
description: str | None = None,
|
||||
prompt: str | None = None,
|
||||
prompt_template_config: PromptTemplateConfig | None = None,
|
||||
prompt_execution_settings: (
|
||||
PromptExecutionSettings | list[PromptExecutionSettings] | dict[str, PromptExecutionSettings] | None
|
||||
) = None,
|
||||
template_format: TEMPLATE_FORMAT_TYPES = KERNEL_TEMPLATE_FORMAT_NAME,
|
||||
prompt_template: PromptTemplateBase | None = None,
|
||||
**kwargs: Any,
|
||||
) -> KernelFunction:
|
||||
"""
|
||||
Create a Kernel Function from a prompt.
|
||||
|
||||
Args:
|
||||
function_name (str): The name of the function
|
||||
plugin_name (str): The name of the plugin
|
||||
description (str | None): The description of the function
|
||||
prompt (str | None): The prompt template.
|
||||
prompt_template_config (PromptTemplateConfig | None): The prompt template configuration
|
||||
prompt_execution_settings (PromptExecutionSettings | list[PromptExecutionSettings]
|
||||
| dict[str, PromptExecutionSettings] | None):
|
||||
The execution settings, will be parsed into a dict.
|
||||
template_format (str | None): The format of the prompt template
|
||||
prompt_template (PromptTemplateBase | None): The prompt template
|
||||
kwargs (Any): Additional arguments
|
||||
|
||||
Returns:
|
||||
KernelFunction: The created Kernel Function
|
||||
"""
|
||||
if prompt_execution_settings is None and (
|
||||
prompt_template_config is None or prompt_template_config.execution_settings is None
|
||||
):
|
||||
prompt_execution_settings = PromptExecutionSettings(extension_data=kwargs)
|
||||
|
||||
function = KernelFunctionFromPrompt(
|
||||
function_name=function_name,
|
||||
plugin_name=plugin_name,
|
||||
description=description,
|
||||
prompt=prompt,
|
||||
template_format=template_format,
|
||||
prompt_template=prompt_template,
|
||||
prompt_template_config=prompt_template_config,
|
||||
prompt_execution_settings=prompt_execution_settings,
|
||||
)
|
||||
|
||||
self.add_plugin(plugin_name or function.plugin_name, [function])
|
||||
|
||||
return function
|
||||
|
||||
def create_function_from_yaml(self, text: str, plugin_name: str) -> KernelFunction:
|
||||
"""
|
||||
Import a plugin from a YAML string.
|
||||
|
||||
Args:
|
||||
text (str): The YAML string
|
||||
|
||||
Returns:
|
||||
KernelFunction: The created Kernel Function
|
||||
|
||||
Raises:
|
||||
PluginInitializationError: If the input YAML string is empty
|
||||
"""
|
||||
if not text:
|
||||
raise PluginInitializationError("The input YAML string is empty")
|
||||
|
||||
try:
|
||||
data = yaml.safe_load(text)
|
||||
except yaml.YAMLError as exc:
|
||||
raise PluginInitializationError(f"Error loading YAML: {exc}") from exc
|
||||
|
||||
if not isinstance(data, dict):
|
||||
raise PluginInitializationError("The YAML content must represent a dictionary")
|
||||
|
||||
try:
|
||||
prompt_template_config = PromptTemplateConfig(**data)
|
||||
except TypeError as exc:
|
||||
raise PluginInitializationError(f"Error initializing PromptTemplateConfig: {exc}") from exc
|
||||
|
||||
return self.create_function_from_prompt(
|
||||
function_name=prompt_template_config.name,
|
||||
plugin_name=plugin_name,
|
||||
description=prompt_template_config.description,
|
||||
prompt_template_config=prompt_template_config,
|
||||
template_format=prompt_template_config.template_format,
|
||||
)
|
||||
|
||||
def register_function_from_method(
|
||||
self,
|
||||
plugin_name: str,
|
||||
method: Callable[..., Any],
|
||||
) -> KernelFunction:
|
||||
"""
|
||||
Creates a native function from the plugin name and registers it with the kernel.
|
||||
|
||||
Args:
|
||||
plugin_name (str | None): The name of the plugin. If empty, a random name will be generated.
|
||||
kernel_function (Callable): The kernel function
|
||||
|
||||
Returns:
|
||||
KernelFunction: The created native function
|
||||
"""
|
||||
if not hasattr(method, "__kernel_function__"):
|
||||
raise FunctionInitializationError(
|
||||
"kernel_function argument must be decorated with @kernel_function",
|
||||
return self.add_plugin(
|
||||
await KernelPlugin.from_openai(
|
||||
plugin_name=plugin_name,
|
||||
plugin_url=plugin_url,
|
||||
plugin_str=plugin_str,
|
||||
execution_parameters=execution_parameters,
|
||||
description=description,
|
||||
)
|
||||
|
||||
function = KernelFunctionFromMethod(
|
||||
method=method,
|
||||
plugin_name=plugin_name,
|
||||
)
|
||||
self.add_plugin(plugin_name or function.plugin_name, [function])
|
||||
|
||||
return function
|
||||
def get_plugin(self, plugin_name: str) -> "KernelPlugin":
|
||||
"""Get a plugin by name.
|
||||
|
||||
Args:
|
||||
plugin_name (str): The name of the plugin
|
||||
|
||||
Returns:
|
||||
KernelPlugin: The plugin
|
||||
|
||||
Raises:
|
||||
KernelPluginNotFoundError: If the plugin is not found
|
||||
|
||||
"""
|
||||
if plugin_name not in self.plugins:
|
||||
raise KernelPluginNotFoundError(f"Plugin '{plugin_name}' not found")
|
||||
return self.plugins[plugin_name]
|
||||
|
||||
def get_function(self, plugin_name: str | None, function_name: str) -> "KernelFunction":
|
||||
"""Get a function by plugin_name and function_name.
|
||||
|
||||
Args:
|
||||
plugin_name (str | None): The name of the plugin
|
||||
function_name (str): The name of the function
|
||||
|
||||
Returns:
|
||||
KernelFunction: The function
|
||||
|
||||
Raises:
|
||||
KernelPluginNotFoundError: If the plugin is not found
|
||||
KernelFunctionNotFoundError: If the function is not found
|
||||
|
||||
"""
|
||||
if plugin_name is None:
|
||||
for plugin in self.plugins.values():
|
||||
if function_name in plugin:
|
||||
return plugin[function_name]
|
||||
raise KernelFunctionNotFoundError(f"Function '{function_name}' not found in any plugin.")
|
||||
if plugin_name not in self.plugins:
|
||||
raise KernelPluginNotFoundError(f"Plugin '{plugin_name}' not found")
|
||||
if function_name not in self.plugins[plugin_name]:
|
||||
raise KernelFunctionNotFoundError(f"Function '{function_name}' not found in plugin '{plugin_name}'")
|
||||
return self.plugins[plugin_name][function_name]
|
||||
|
||||
def get_function_from_fully_qualified_function_name(self, fully_qualified_function_name: str) -> "KernelFunction":
|
||||
"""Get a function by its fully qualified name (<plugin_name>-<function_name>).
|
||||
|
||||
Args:
|
||||
fully_qualified_function_name (str): The fully qualified name of the function,
|
||||
if there is no '-' in the name, it is assumed that it is only a function_name.
|
||||
|
||||
Returns:
|
||||
KernelFunction: The function
|
||||
|
||||
Raises:
|
||||
KernelPluginNotFoundError: If the plugin is not found
|
||||
KernelFunctionNotFoundError: If the function is not found
|
||||
|
||||
"""
|
||||
names = fully_qualified_function_name.split("-", maxsplit=1)
|
||||
if len(names) == 1:
|
||||
plugin_name = None
|
||||
function_name = names[0]
|
||||
else:
|
||||
plugin_name = names[0]
|
||||
function_name = names[1]
|
||||
return self.get_function(plugin_name, function_name)
|
||||
|
||||
def get_list_of_function_metadata(
|
||||
self, include_prompt: bool = True, include_native: bool = True
|
||||
) -> list[KernelFunctionMetadata]:
|
||||
"""
|
||||
Get a list of the function metadata in the plugin collection
|
||||
|
||||
Args:
|
||||
include_prompt (bool): Whether to include semantic functions in the list.
|
||||
include_native (bool): Whether to include native functions in the list.
|
||||
|
||||
Returns:
|
||||
A list of KernelFunctionMetadata objects in the collection.
|
||||
"""
|
||||
if not self.plugins:
|
||||
return []
|
||||
return [
|
||||
func.metadata
|
||||
for plugin in self.plugins.values()
|
||||
for func in plugin.functions.values()
|
||||
if (include_prompt and func.is_prompt) or (include_native and not func.is_prompt)
|
||||
]
|
||||
|
||||
# endregion
|
||||
# region Services
|
||||
|
||||
def select_ai_service(
|
||||
self, function: KernelFunction, arguments: KernelArguments
|
||||
self, function: "KernelFunction", arguments: KernelArguments
|
||||
) -> tuple[ALL_SERVICE_TYPES, PromptExecutionSettings]:
|
||||
"""Uses the AI service selector to select a service for the function."""
|
||||
return self.ai_service_selector.select_ai_service(self, function, arguments)
|
||||
|
||||
@@ -74,13 +74,13 @@ class ActionPlanner:
|
||||
extension_data={"max_tokens": self.config.max_tokens, "stop_sequences": self._stop_sequence},
|
||||
)
|
||||
|
||||
self._planner_function = kernel.create_function_from_prompt(
|
||||
function_name="ActionPlanner",
|
||||
kernel.add_plugin(self, self.RESTRICTED_PLUGIN_NAME)
|
||||
self._planner_function = kernel.add_function(
|
||||
plugin_name=self.RESTRICTED_PLUGIN_NAME,
|
||||
function_name="ActionPlanner",
|
||||
prompt=self._prompt_template,
|
||||
prompt_execution_settings=execute_settings,
|
||||
)
|
||||
kernel.import_plugin_from_object(self, self.RESTRICTED_PLUGIN_NAME)
|
||||
|
||||
self._kernel = kernel
|
||||
self._arguments = KernelArguments()
|
||||
@@ -213,7 +213,7 @@ class ActionPlanner:
|
||||
def list_of_functions(self, goal: Annotated[str, "The current goal processed by the planner"]) -> str:
|
||||
available_functions = [
|
||||
self._create_function_string(func)
|
||||
for func in self._kernel.plugins.get_list_of_function_metadata()
|
||||
for func in self._kernel.get_list_of_function_metadata()
|
||||
if (
|
||||
func.plugin_name != self.RESTRICTED_PLUGIN_NAME
|
||||
and func.plugin_name not in self.config.excluded_plugins
|
||||
|
||||
@@ -142,9 +142,7 @@ class BasicPlanner:
|
||||
# Get a dictionary of plugin names to all native and semantic functions
|
||||
if not kernel.plugins:
|
||||
return ""
|
||||
all_functions = {
|
||||
f"{func.plugin_name}.{func.name}": func for func in kernel.plugins.get_list_of_function_metadata()
|
||||
}
|
||||
all_functions = {f"{func.plugin_name}.{func.name}": func for func in kernel.get_list_of_function_metadata()}
|
||||
all_functions_descriptions_dict = {key: func.description for key, func in all_functions.items()}
|
||||
all_functions_params_dict = {key: func.parameters for key, func in all_functions.items()}
|
||||
|
||||
@@ -190,7 +188,7 @@ class BasicPlanner:
|
||||
)
|
||||
|
||||
# Create the prompt function for the planner with the given prompt
|
||||
planner = kernel.create_function_from_prompt(
|
||||
planner = kernel.add_function(
|
||||
plugin_name="PlannerPlugin",
|
||||
function_name="CreatePlan",
|
||||
prompt_template_config=prompt_template_config,
|
||||
@@ -225,7 +223,7 @@ class BasicPlanner:
|
||||
|
||||
for subtask in subtasks:
|
||||
plugin_name, function_name = subtask["function"].split(".")
|
||||
kernel_function = kernel.func(plugin_name, function_name)
|
||||
kernel_function = kernel.get_function(plugin_name, function_name)
|
||||
# Get the arguments dictionary for the function
|
||||
args = subtask.get("args", None)
|
||||
if args:
|
||||
|
||||
+3
-6
@@ -15,10 +15,7 @@ from semantic_kernel.connectors.ai.open_ai.prompt_execution_settings.open_ai_pro
|
||||
)
|
||||
from semantic_kernel.connectors.ai.open_ai.services.azure_chat_completion import AzureChatCompletion
|
||||
from semantic_kernel.connectors.ai.open_ai.services.open_ai_chat_completion import OpenAIChatCompletion
|
||||
from semantic_kernel.connectors.ai.open_ai.utils import (
|
||||
get_function_calling_object,
|
||||
get_tool_call_object,
|
||||
)
|
||||
from semantic_kernel.connectors.ai.open_ai.utils import get_function_calling_object, get_tool_call_object
|
||||
from semantic_kernel.contents.chat_history import ChatHistory
|
||||
from semantic_kernel.exceptions.planner_exceptions import PlannerInvalidConfigurationError
|
||||
from semantic_kernel.functions.kernel_arguments import KernelArguments
|
||||
@@ -138,7 +135,7 @@ class FunctionCallingStepwisePlanner(KernelBaseModel):
|
||||
|
||||
# Clone the kernel so that we can add planner-specific plugins without affecting the original kernel instance
|
||||
cloned_kernel = copy(kernel)
|
||||
cloned_kernel.import_plugin_from_object(UserInteraction(), "UserInteraction")
|
||||
cloned_kernel.add_plugin(UserInteraction(), "UserInteraction")
|
||||
|
||||
# Create and invoke a kernel function to generate the initial plan
|
||||
initial_plan = await self._generate_plan(question=question, kernel=cloned_kernel, arguments=arguments)
|
||||
@@ -224,7 +221,7 @@ class FunctionCallingStepwisePlanner(KernelBaseModel):
|
||||
if "default" in prompt_template_config.execution_settings:
|
||||
settings = prompt_template_config.execution_settings.pop("default")
|
||||
prompt_template_config.execution_settings[self.service_id] = settings
|
||||
return kernel.create_function_from_prompt(
|
||||
return kernel.add_function(
|
||||
function_name="create_plan",
|
||||
plugin_name="sequential_planner",
|
||||
description="Create a plan for the given goal",
|
||||
|
||||
@@ -61,7 +61,7 @@ class PlannerKernelExtension:
|
||||
|
||||
available_functions = [
|
||||
func
|
||||
for func in kernel.plugins.get_list_of_function_metadata()
|
||||
for func in kernel.get_list_of_function_metadata()
|
||||
if (func.plugin_name not in excluded_plugins and func.name not in excluded_functions)
|
||||
]
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ class SequentialPlanner:
|
||||
settings = prompt_config.execution_settings.pop("default")
|
||||
prompt_config.execution_settings[service_id] = settings
|
||||
|
||||
return self._kernel.create_function_from_prompt(
|
||||
return self._kernel.add_function(
|
||||
plugin_name=self.RESTRICTED_PLUGIN_NAME,
|
||||
function_name=self.RESTRICTED_PLUGIN_NAME,
|
||||
prompt_template_config=prompt_config,
|
||||
|
||||
@@ -64,7 +64,7 @@ class SequentialPlannerKernelExtension:
|
||||
|
||||
available_functions = [
|
||||
func
|
||||
for func in kernel.plugins.get_list_of_function_metadata()
|
||||
for func in kernel.get_list_of_function_metadata()
|
||||
if (func.plugin_name not in excluded_plugins and func.name not in excluded_functions)
|
||||
]
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ class SequentialPlanParser:
|
||||
raise PlannerInvalidPlanError(f"Failed to find function '{plugin_function_name}'.") from exc
|
||||
else:
|
||||
try:
|
||||
func = kernel.func_from_fully_qualified_function_name(plugin_function_name)
|
||||
func = kernel.get_function_from_fully_qualified_function_name(plugin_function_name)
|
||||
except (KernelFunctionNotFoundError, KernelPluginNotFoundError) as exc:
|
||||
if allow_missing_functions:
|
||||
plan.add_steps([Plan.from_goal(plugin_function_name)])
|
||||
|
||||
@@ -86,7 +86,7 @@ class StepwisePlanner:
|
||||
prompt_config.template = prompt_template
|
||||
|
||||
self._system_step_function = self.import_function_from_prompt(kernel, "StepwiseStep", prompt_config)
|
||||
self._native_functions = self._kernel.import_plugin_from_object(self, RESTRICTED_PLUGIN_NAME)
|
||||
self._native_functions = self._kernel.add_plugin(self, RESTRICTED_PLUGIN_NAME)
|
||||
|
||||
self._arguments = KernelArguments()
|
||||
|
||||
@@ -324,7 +324,7 @@ class StepwisePlanner:
|
||||
raise PlannerExecutionException(f"The function '{action_name}' was not found.")
|
||||
|
||||
try:
|
||||
function = self._kernel.func(target_function.plugin_name, target_function.name)
|
||||
function = self._kernel.get_function(target_function.plugin_name, target_function.name)
|
||||
action_arguments = self.create_action_arguments(action_variables)
|
||||
|
||||
result = await function.invoke(self._kernel, action_arguments)
|
||||
@@ -360,7 +360,7 @@ class StepwisePlanner:
|
||||
excluded_functions = self.config.excluded_functions or []
|
||||
available_functions = [
|
||||
func
|
||||
for func in self._kernel.plugins.get_list_of_function_metadata()
|
||||
for func in self._kernel.get_list_of_function_metadata()
|
||||
if (func.plugin_name not in excluded_plugins and func.name not in excluded_functions)
|
||||
]
|
||||
available_functions = sorted(available_functions, key=lambda x: (x.plugin_name, x.name))
|
||||
@@ -379,9 +379,10 @@ class StepwisePlanner:
|
||||
function_name: str,
|
||||
config: PromptTemplateConfig = None,
|
||||
) -> "KernelFunction":
|
||||
return kernel.create_function_from_prompt(
|
||||
kernel.add_function(
|
||||
plugin_name=RESTRICTED_PLUGIN_NAME, function_name=function_name, prompt_template_config=config
|
||||
)
|
||||
return kernel.get_function(RESTRICTED_PLUGIN_NAME, function_name)
|
||||
|
||||
def to_manual_string(self, function: KernelFunctionMetadata) -> str:
|
||||
inputs = [
|
||||
|
||||
@@ -72,16 +72,16 @@ class HandlebarsPromptTemplate(PromptTemplateBase):
|
||||
"""
|
||||
if not self._template_compiler:
|
||||
return ""
|
||||
if not arguments:
|
||||
if arguments is None:
|
||||
arguments = KernelArguments()
|
||||
helpers = {}
|
||||
for plugin in kernel.plugins:
|
||||
for plugin in kernel.plugins.values():
|
||||
helpers.update(
|
||||
{
|
||||
function.fully_qualified_name: create_template_helper_from_function(
|
||||
function, kernel, arguments, self.prompt_template_config.template_format
|
||||
)
|
||||
for function in plugin.functions.values()
|
||||
for function in plugin
|
||||
}
|
||||
)
|
||||
helpers.update(HANDLEBAR_SYSTEM_HELPERS)
|
||||
|
||||
@@ -77,11 +77,11 @@ class Jinja2PromptTemplate(PromptTemplateBase):
|
||||
"""
|
||||
if not self._env:
|
||||
return ""
|
||||
if not arguments:
|
||||
if arguments is None:
|
||||
arguments = KernelArguments()
|
||||
helpers = {}
|
||||
helpers.update(JINJA2_SYSTEM_HELPERS)
|
||||
for plugin in kernel.plugins:
|
||||
for plugin in kernel.plugins.values():
|
||||
helpers.update(
|
||||
{
|
||||
function.fully_qualified_name.replace("-", "_"): create_template_helper_from_function(
|
||||
@@ -90,7 +90,7 @@ class Jinja2PromptTemplate(PromptTemplateBase):
|
||||
arguments,
|
||||
self.prompt_template_config.template_format,
|
||||
)
|
||||
for function in plugin.functions.values()
|
||||
for function in plugin
|
||||
}
|
||||
)
|
||||
try:
|
||||
|
||||
@@ -92,7 +92,7 @@ class KernelPromptTemplate(PromptTemplateBase):
|
||||
Returns:
|
||||
The prompt template ready to be used for an AI request
|
||||
"""
|
||||
if not arguments:
|
||||
if arguments is None:
|
||||
arguments = KernelArguments()
|
||||
return await self.render_blocks(self._blocks, kernel, arguments)
|
||||
|
||||
|
||||
@@ -2,21 +2,19 @@
|
||||
|
||||
import logging
|
||||
from copy import copy
|
||||
from typing import TYPE_CHECKING, Any, ClassVar, List, Optional
|
||||
from typing import TYPE_CHECKING, Any, ClassVar, List
|
||||
|
||||
from pydantic import Field, field_validator, model_validator
|
||||
|
||||
from semantic_kernel.exceptions import CodeBlockRenderException, CodeBlockTokenError
|
||||
from semantic_kernel.exceptions.kernel_exceptions import KernelFunctionNotFoundError, KernelPluginNotFoundError
|
||||
from semantic_kernel.functions.kernel_function_metadata import KernelFunctionMetadata
|
||||
from semantic_kernel.functions.kernel_plugin_collection import KernelPluginCollection
|
||||
from semantic_kernel.template_engine.blocks.block import Block
|
||||
from semantic_kernel.template_engine.blocks.block_types import BlockTypes
|
||||
from semantic_kernel.template_engine.blocks.function_id_block import FunctionIdBlock
|
||||
from semantic_kernel.template_engine.code_tokenizer import CodeTokenizer
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from semantic_kernel.functions.kernel_arguments import KernelArguments
|
||||
from semantic_kernel.functions.kernel_function import KernelFunction
|
||||
from semantic_kernel.kernel import Kernel
|
||||
|
||||
logger: logging.Logger = logging.getLogger(__name__)
|
||||
@@ -116,11 +114,12 @@ these will be ignored."
|
||||
|
||||
async def _render_function_call(self, kernel: "Kernel", arguments: "KernelArguments"):
|
||||
function_block = self.tokens[0]
|
||||
function = self._get_function_from_plugin_collection(kernel.plugins, function_block)
|
||||
if not function:
|
||||
try:
|
||||
function = kernel.get_function(function_block.plugin_name, function_block.function_name)
|
||||
except (KernelFunctionNotFoundError, KernelPluginNotFoundError) as exc:
|
||||
error_msg = f"Function `{function_block.content}` not found"
|
||||
logger.error(error_msg)
|
||||
raise CodeBlockRenderException(error_msg)
|
||||
raise CodeBlockRenderException(error_msg) from exc
|
||||
|
||||
arguments_clone = copy(arguments)
|
||||
if len(self.tokens) > 1:
|
||||
@@ -152,26 +151,3 @@ these will be ignored."
|
||||
arguments[token.name] = rendered_value
|
||||
|
||||
return arguments
|
||||
|
||||
def _get_function_from_plugin_collection(
|
||||
self, plugins: KernelPluginCollection, function_block: FunctionIdBlock
|
||||
) -> Optional["KernelFunction"]:
|
||||
"""
|
||||
Get the function from the plugin collection
|
||||
|
||||
Args:
|
||||
plugins: The plugin collection
|
||||
function_block: The function block that contains the function name
|
||||
|
||||
Returns:
|
||||
The function if it exists, None otherwise.
|
||||
"""
|
||||
if function_block.plugin_name is not None and len(function_block.plugin_name) > 0:
|
||||
return plugins[function_block.plugin_name][function_block.function_name]
|
||||
else:
|
||||
# We now require a plug-in name, but if one isn't set then we'll try to find the function
|
||||
for plugin in plugins:
|
||||
if function_block.function_name in plugin:
|
||||
return plugin[function_block.function_name]
|
||||
|
||||
return None
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import sys
|
||||
from typing import Optional
|
||||
|
||||
from semantic_kernel.functions.kernel_function_decorator import kernel_function
|
||||
|
||||
if sys.version_info >= (3, 9):
|
||||
from typing import Annotated
|
||||
else:
|
||||
from typing_extensions import Annotated
|
||||
|
||||
|
||||
class TestNativeEchoBotPlugin:
|
||||
"""
|
||||
Description: Test Native Plugin for testing purposes
|
||||
"""
|
||||
|
||||
def __init__(self, static_input: Optional[str] = None):
|
||||
self.static_input = static_input or ""
|
||||
|
||||
@kernel_function(
|
||||
description="Echo for input text with static",
|
||||
name="echo",
|
||||
)
|
||||
def echo(self, text: Annotated[str, "The text to echo"]) -> str:
|
||||
"""
|
||||
Echo for input text with a static input
|
||||
|
||||
Example:
|
||||
"hello world" => "hello world"
|
||||
Args:
|
||||
text -- The text to echo
|
||||
|
||||
Returns:
|
||||
input text
|
||||
"""
|
||||
return self.static_input + text
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"description": "Test Description",
|
||||
"execution_settings": {
|
||||
"default": {
|
||||
"max_tokens": 123,
|
||||
"temperature": 0.0,
|
||||
"top_p": 1.0,
|
||||
"presence_penalty": 0.0,
|
||||
"frequency_penalty": 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{{$input}}
|
||||
|
||||
==
|
||||
Test prompt.
|
||||
==
|
||||
@@ -0,0 +1,32 @@
|
||||
import sys
|
||||
|
||||
from semantic_kernel.functions.kernel_function_decorator import kernel_function
|
||||
|
||||
if sys.version_info >= (3, 9):
|
||||
from typing import Annotated
|
||||
else:
|
||||
from typing_extensions import Annotated
|
||||
|
||||
|
||||
class TestNativeEchoBotPlugin:
|
||||
"""
|
||||
Description: Test Native Plugin for testing purposes
|
||||
"""
|
||||
|
||||
@kernel_function(
|
||||
description="Echo for input text",
|
||||
name="echoAsync",
|
||||
)
|
||||
async def echo(self, text: Annotated[str, "The text to echo"]) -> str:
|
||||
"""
|
||||
Echo for input text
|
||||
|
||||
Example:
|
||||
"hello world" => "hello world"
|
||||
Args:
|
||||
text -- The text to echo
|
||||
|
||||
Returns:
|
||||
input text
|
||||
"""
|
||||
return text
|
||||
@@ -0,0 +1,12 @@
|
||||
name: TestFunctionYaml
|
||||
template_format: semantic-kernel
|
||||
template: |
|
||||
{{$input}}
|
||||
description: A test function from a yaml file.
|
||||
execution_settings:
|
||||
default:
|
||||
temperature: 0.6
|
||||
max_tokens: 123
|
||||
top_p: 1.0
|
||||
presence_penalty: 0.0
|
||||
frequency_penalty: 2.0
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"description": "Test Description",
|
||||
"execution_settings": {
|
||||
"default": {
|
||||
"max_tokens": 123,
|
||||
"temperature": 0.0,
|
||||
"top_p": 1.0,
|
||||
"presence_penalty": 0.0,
|
||||
"frequency_penalty": 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{{$input}}
|
||||
|
||||
==
|
||||
Test prompt.
|
||||
==
|
||||
@@ -115,6 +115,8 @@ def create_mock_function() -> Callable:
|
||||
mock_function.description = kernel_function_metadata.description
|
||||
mock_function.invoke.return_value = FunctionResult(function=mock_function.metadata, value=value, metadata={})
|
||||
mock_function.invoke_stream = stream_func
|
||||
mock_function.function_copy.return_value = mock_function
|
||||
mock_function.__kernel_function__ = True
|
||||
|
||||
return mock_function
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ def setup_gp_text_completion_function(kernel: Kernel, get_gp_config):
|
||||
prompt_template_config = PromptTemplateConfig(template=prompt, execution_settings=exec_settings)
|
||||
|
||||
# Create the semantic function
|
||||
text2text_function = kernel.create_function_from_prompt(
|
||||
text2text_function = kernel.add_function(
|
||||
function_name="hello", plugin_name="plugin", prompt_template_config=prompt_template_config
|
||||
)
|
||||
|
||||
|
||||
@@ -7,6 +7,9 @@ from openai import AsyncAzureOpenAI
|
||||
from test_utils import retry
|
||||
|
||||
import semantic_kernel.connectors.ai.open_ai as sk_oai
|
||||
from semantic_kernel.connectors.ai.open_ai.prompt_execution_settings.azure_chat_prompt_execution_settings import (
|
||||
AzureChatPromptExecutionSettings,
|
||||
)
|
||||
from semantic_kernel.connectors.ai.open_ai.utils import get_tool_call_object
|
||||
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
|
||||
from semantic_kernel.core_plugins.math_plugin import MathPlugin
|
||||
@@ -46,13 +49,11 @@ async def test_azure_e2e_chat_completion_with_plugin(setup_tldr_function_for_oai
|
||||
)
|
||||
|
||||
# Create the semantic function
|
||||
tldr_function = kernel.create_function_from_prompt(
|
||||
function_name="tldr", plugin_name="plugin", prompt_template_config=prompt_template_config
|
||||
)
|
||||
kernel.add_function(function_name="tldr", plugin_name="plugin", prompt_template_config=prompt_template_config)
|
||||
|
||||
arguments = KernelArguments(input=text_to_summarize)
|
||||
|
||||
summary = await retry(lambda: kernel.invoke(tldr_function, arguments))
|
||||
summary = await retry(lambda: kernel.invoke(function_name="tldr", plugin_name="plugin", arguments=arguments))
|
||||
output = str(summary).strip()
|
||||
print(f"TLDR using input string: '{output}'")
|
||||
assert "First Law" not in output and ("human" in output or "Human" in output or "preserve" in output)
|
||||
@@ -102,13 +103,11 @@ async def test_azure_e2e_chat_completion_with_plugin_and_provided_client(
|
||||
)
|
||||
|
||||
# Create the semantic function
|
||||
tldr_function = kernel.create_function_from_prompt(
|
||||
function_name="tldr", plugin_name="plugin", prompt_template_config=prompt_template_config
|
||||
)
|
||||
kernel.add_function(function_name="tldr", plugin_name="plugin", prompt_template_config=prompt_template_config)
|
||||
|
||||
arguments = KernelArguments(input=text_to_summarize)
|
||||
|
||||
summary = await retry(lambda: kernel.invoke(tldr_function, arguments))
|
||||
summary = await retry(lambda: kernel.invoke(function_name="tldr", plugin_name="plugin", arguments=arguments))
|
||||
output = str(summary).strip()
|
||||
print(f"TLDR using input string: '{output}'")
|
||||
assert "First Law" not in output and ("human" in output or "Human" in output or "preserve" in output)
|
||||
@@ -116,9 +115,7 @@ async def test_azure_e2e_chat_completion_with_plugin_and_provided_client(
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_azure_oai_chat_service_with_tool_call(setup_tldr_function_for_oai_models, get_aoai_config):
|
||||
kernel, _, _ = setup_tldr_function_for_oai_models
|
||||
|
||||
async def test_azure_oai_chat_service_with_tool_call(kernel: Kernel, get_aoai_config):
|
||||
_, api_key, endpoint = get_aoai_config
|
||||
|
||||
if "Python_Integration_Tests" in os.environ:
|
||||
@@ -147,9 +144,9 @@ async def test_azure_oai_chat_service_with_tool_call(setup_tldr_function_for_oai
|
||||
),
|
||||
)
|
||||
|
||||
kernel.import_plugin_from_object(MathPlugin(), plugin_name="math")
|
||||
kernel.add_plugin(MathPlugin(), plugin_name="math")
|
||||
|
||||
execution_settings = sk_oai.AzureChatPromptExecutionSettings(
|
||||
execution_settings = AzureChatPromptExecutionSettings(
|
||||
service_id="chat_completion",
|
||||
max_tokens=2000,
|
||||
temperature=0.7,
|
||||
@@ -165,11 +162,13 @@ async def test_azure_oai_chat_service_with_tool_call(setup_tldr_function_for_oai
|
||||
)
|
||||
|
||||
# Create the prompt function
|
||||
tldr_function = kernel.create_function_from_prompt(
|
||||
kernel.add_function(
|
||||
function_name="math_fun", plugin_name="math_int_test", prompt_template_config=prompt_template_config
|
||||
)
|
||||
|
||||
summary = await retry(lambda: kernel.invoke(tldr_function, input="what is 1+1?"))
|
||||
summary = await retry(
|
||||
lambda: kernel.invoke(function_name="math_fun", plugin_name="math_int_test", input="what is 1+1?")
|
||||
)
|
||||
output = str(summary).strip()
|
||||
print(f"Math output: '{output}'")
|
||||
assert "2" in output
|
||||
@@ -206,10 +205,10 @@ async def test_azure_oai_chat_service_with_tool_call_streaming(kernel: Kernel, g
|
||||
),
|
||||
)
|
||||
|
||||
kernel.import_plugin_from_object(MathPlugin(), plugin_name="Math")
|
||||
kernel.add_plugin(MathPlugin(), plugin_name="Math")
|
||||
|
||||
# Create the prompt function
|
||||
chat_func = kernel.create_function_from_prompt(prompt="{{$input}}", function_name="chat", plugin_name="chat")
|
||||
kernel.add_function(prompt="{{$input}}", function_name="chat", plugin_name="chat")
|
||||
execution_settings = sk_oai.AzureChatPromptExecutionSettings(
|
||||
service_id="chat_completion",
|
||||
max_tokens=2000,
|
||||
@@ -223,7 +222,7 @@ async def test_azure_oai_chat_service_with_tool_call_streaming(kernel: Kernel, g
|
||||
arguments = KernelArguments(input="what is 101+102?", settings=execution_settings)
|
||||
|
||||
result = None
|
||||
async for message in kernel.invoke_stream(chat_func, arguments=arguments):
|
||||
async for message in kernel.invoke_stream(function_name="chat", plugin_name="chat", arguments=arguments):
|
||||
result = message[0] if not result else result + message[0]
|
||||
output = str(result)
|
||||
|
||||
|
||||
@@ -130,10 +130,8 @@ async def create_with_data_chat_function(get_aoai_config, kernel: Kernel, create
|
||||
)
|
||||
|
||||
# Create the semantic function
|
||||
chat_function = kernel.create_function_from_prompt(
|
||||
function_name="story", plugin_name="plugin", prompt_template_config=prompt_template_config
|
||||
)
|
||||
|
||||
kernel.add_function(function_name="story", plugin_name="plugin", prompt_template_config=prompt_template_config)
|
||||
chat_function = kernel.get_function("plugin", "story")
|
||||
return chat_function, kernel, collection, memory_store
|
||||
except:
|
||||
await memory_store.delete_collection(collection)
|
||||
|
||||
@@ -46,7 +46,7 @@ async def test_azure_e2e_text_completion_with_plugin(setup_tldr_function_for_oai
|
||||
)
|
||||
|
||||
# Create the semantic function
|
||||
tldr_function = kernel.create_function_from_prompt(
|
||||
tldr_function = kernel.add_function(
|
||||
function_name="story", plugin_name="plugin", prompt_template_config=prompt_template_config
|
||||
)
|
||||
|
||||
@@ -103,7 +103,7 @@ async def test_azure_e2e_text_completion_with_plugin_with_provided_client(
|
||||
)
|
||||
|
||||
# Create the semantic function
|
||||
tldr_function = kernel.create_function_from_prompt(
|
||||
tldr_function = kernel.add_function(
|
||||
function_name="tldr", plugin_name="plugin", prompt_template_config=prompt_template_config
|
||||
)
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ async def test_azure_summarize_conversation_using_plugin(setup_summarize_convers
|
||||
),
|
||||
)
|
||||
|
||||
conversationSummaryPlugin = kernel.import_plugin_from_object(
|
||||
conversationSummaryPlugin = kernel.add_plugin(
|
||||
ConversationSummaryPlugin(kernel, prompt_template_config), "conversationSummary"
|
||||
)
|
||||
|
||||
@@ -92,7 +92,7 @@ async def test_oai_summarize_conversation_using_plugin(
|
||||
),
|
||||
)
|
||||
|
||||
conversationSummaryPlugin = kernel.import_plugin_from_object(
|
||||
conversationSummaryPlugin = kernel.add_plugin(
|
||||
ConversationSummaryPlugin(kernel, prompt_template_config), "conversationSummary"
|
||||
)
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ async def test_gp_chat_service_with_plugins(setup_tldr_function_for_oai_models,
|
||||
prompt_template_config = PromptTemplateConfig(template=prompt, execution_settings=exec_settings)
|
||||
|
||||
# Create the semantic function
|
||||
tldr_function = kernel.create_function_from_prompt(
|
||||
tldr_function = kernel.add_function(
|
||||
function_name="tldr", plugin_name="plugin", prompt_template_config=prompt_template_config
|
||||
)
|
||||
|
||||
|
||||
@@ -6,9 +6,7 @@ from openai import AsyncOpenAI
|
||||
from test_utils import retry
|
||||
|
||||
import semantic_kernel.connectors.ai.open_ai as sk_oai
|
||||
from semantic_kernel.connectors.ai.open_ai.utils import (
|
||||
get_tool_call_object,
|
||||
)
|
||||
from semantic_kernel.connectors.ai.open_ai.utils import get_tool_call_object
|
||||
from semantic_kernel.connectors.ai.prompt_execution_settings import PromptExecutionSettings
|
||||
from semantic_kernel.contents.chat_history import ChatHistory
|
||||
from semantic_kernel.core_plugins.math_plugin import MathPlugin
|
||||
@@ -38,7 +36,7 @@ async def test_oai_chat_service_with_plugins(setup_tldr_function_for_oai_models,
|
||||
)
|
||||
|
||||
# Create the semantic function
|
||||
tldr_function = kernel.create_function_from_prompt(
|
||||
tldr_function = kernel.add_function(
|
||||
function_name="story", plugin_name="plugin", prompt_template_config=prompt_template_config
|
||||
)
|
||||
|
||||
@@ -65,7 +63,7 @@ async def test_oai_chat_service_with_tool_call(setup_tldr_function_for_oai_model
|
||||
),
|
||||
)
|
||||
|
||||
kernel.import_plugin_from_object(MathPlugin(), plugin_name="math")
|
||||
kernel.add_plugin(MathPlugin(), plugin_name="math")
|
||||
|
||||
execution_settings = sk_oai.OpenAIChatPromptExecutionSettings(
|
||||
service_id="chat-gpt",
|
||||
@@ -83,7 +81,7 @@ async def test_oai_chat_service_with_tool_call(setup_tldr_function_for_oai_model
|
||||
)
|
||||
|
||||
# Create the prompt function
|
||||
tldr_function = kernel.create_function_from_prompt(
|
||||
tldr_function = kernel.add_function(
|
||||
function_name="math_fun", plugin_name="math_int_test", prompt_template_config=prompt_template_config
|
||||
)
|
||||
|
||||
@@ -110,7 +108,7 @@ async def test_oai_chat_service_with_tool_call_streaming(setup_tldr_function_for
|
||||
),
|
||||
)
|
||||
|
||||
kernel.import_plugin_from_object(MathPlugin(), plugin_name="math")
|
||||
kernel.add_plugin(MathPlugin(), plugin_name="math")
|
||||
|
||||
execution_settings = sk_oai.OpenAIChatPromptExecutionSettings(
|
||||
service_id="chat-gpt",
|
||||
@@ -128,7 +126,7 @@ async def test_oai_chat_service_with_tool_call_streaming(setup_tldr_function_for
|
||||
)
|
||||
|
||||
# Create the prompt function
|
||||
tldr_function = kernel.create_function_from_prompt(
|
||||
tldr_function = kernel.add_function(
|
||||
function_name="math_fun", plugin_name="math_int_test", prompt_template_config=prompt_template_config
|
||||
)
|
||||
|
||||
@@ -175,7 +173,7 @@ async def test_oai_chat_service_with_plugins_with_provided_client(setup_tldr_fun
|
||||
)
|
||||
|
||||
# Create the semantic function
|
||||
tldr_function = kernel.create_function_from_prompt(
|
||||
tldr_function = kernel.add_function(
|
||||
function_name="story",
|
||||
plugin_name="story_plugin",
|
||||
prompt_template_config=prompt_template_config,
|
||||
@@ -220,7 +218,7 @@ async def test_oai_chat_stream_service_with_plugins(setup_tldr_function_for_oai_
|
||||
)
|
||||
|
||||
# Create the prompt function
|
||||
tldr_function = kernel.create_function_from_prompt(
|
||||
tldr_function = kernel.add_function(
|
||||
function_name="story",
|
||||
plugin_name="story_plugin",
|
||||
prompt_template_config=prompt_template_config,
|
||||
@@ -260,9 +258,9 @@ async def test_oai_chat_service_with_yaml_jinja2(setup_tldr_function_for_oai_mod
|
||||
overwrite=True, # Overwrite the service if it already exists since add service says it does
|
||||
)
|
||||
|
||||
plugins_directory = os.path.join(os.path.dirname(__file__), "../../assets/test_plugins", "TestPlugin")
|
||||
plugins_directory = os.path.join(os.path.dirname(__file__), "../../assets/test_plugins")
|
||||
|
||||
plugin = kernel.import_plugin_from_prompt_directory(plugins_directory, "TestFunctionYamlJinja2")
|
||||
plugin = kernel.add_plugin(parent_directory=plugins_directory, plugin_name="TestFunctionYamlJinja2")
|
||||
assert plugin is not None
|
||||
assert plugin["TestFunctionJinja2"] is not None
|
||||
|
||||
@@ -299,9 +297,9 @@ async def test_oai_chat_service_with_yaml_handlebars(setup_tldr_function_for_oai
|
||||
overwrite=True, # Overwrite the service if it already exists since add service says it does
|
||||
)
|
||||
|
||||
plugins_directory = os.path.join(os.path.dirname(__file__), "../../assets/test_plugins", "TestPlugin")
|
||||
plugins_directory = os.path.join(os.path.dirname(__file__), "../../assets/test_plugins")
|
||||
|
||||
plugin = kernel.import_plugin_from_prompt_directory(plugins_directory, "TestFunctionYamlHandlebars")
|
||||
plugin = kernel.add_plugin(parent_directory=plugins_directory, plugin_name="TestFunctionYamlHandlebars")
|
||||
assert plugin is not None
|
||||
assert plugin["TestFunctionHandlebars"] is not None
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ async def test_oai_text_completion_with_plugins(setup_tldr_function_for_oai_mode
|
||||
)
|
||||
|
||||
# Create the semantic function
|
||||
tldr_function = kernel.create_function_from_prompt(
|
||||
tldr_function = kernel.add_function(
|
||||
function_name="story", plugin_name="plugin", prompt_template_config=prompt_template_config
|
||||
)
|
||||
|
||||
@@ -84,7 +84,7 @@ async def test_oai_text_completion_with_plugins_with_provided_client(
|
||||
)
|
||||
|
||||
# Create the semantic function
|
||||
tldr_function = kernel.create_function_from_prompt(
|
||||
tldr_function = kernel.add_function(
|
||||
function_name="story",
|
||||
plugin_name="plugin",
|
||||
prompt_template_config=prompt_template_config,
|
||||
@@ -134,7 +134,7 @@ async def test_oai_text_stream_completion_with_plugins(setup_tldr_function_for_o
|
||||
)
|
||||
|
||||
# Create the semantic function
|
||||
tldr_function = kernel.create_function_from_prompt(
|
||||
tldr_function = kernel.add_function(
|
||||
function_name="story",
|
||||
plugin_name="plugin",
|
||||
prompt_template_config=prompt_template_config,
|
||||
|
||||
@@ -30,7 +30,7 @@ async def test_azure_text_embedding_service(kernel: Kernel, get_aoai_config):
|
||||
kernel.add_service(embeddings_gen)
|
||||
|
||||
memory = SemanticTextMemory(storage=sk.memory.VolatileMemoryStore(), embeddings_generator=embeddings_gen)
|
||||
kernel.import_plugin_from_object(sk.core_plugins.TextMemoryPlugin(memory), "TextMemoryPlugin")
|
||||
kernel.add_plugin(sk.core_plugins.TextMemoryPlugin(memory), "TextMemoryPlugin")
|
||||
|
||||
await memory.save_information(collection="generic", id="info1", text="My budget for 2024 is $100,000")
|
||||
await memory.save_reference(
|
||||
@@ -66,7 +66,7 @@ async def test_azure_text_embedding_service_with_provided_client(kernel: Kernel,
|
||||
|
||||
kernel.add_service(embeddings_gen)
|
||||
memory = SemanticTextMemory(storage=sk.memory.VolatileMemoryStore(), embeddings_generator=embeddings_gen)
|
||||
kernel.import_plugin_from_object(sk.core_plugins.TextMemoryPlugin(memory), "TextMemoryPlugin")
|
||||
kernel.add_plugin(sk.core_plugins.TextMemoryPlugin(memory), "TextMemoryPlugin")
|
||||
|
||||
await memory.save_information(collection="generic", id="info1", text="My budget for 2024 is $100,000")
|
||||
await memory.save_reference(
|
||||
|
||||
@@ -29,7 +29,7 @@ async def test_gp_embedding_service(kernel: Kernel, get_gp_config):
|
||||
kernel.add_service(palm_text_embed)
|
||||
|
||||
memory = SemanticTextMemory(storage=sk.memory.VolatileMemoryStore(), embeddings_generator=palm_text_embed)
|
||||
kernel.import_plugin_from_object(sk.core_plugins.TextMemoryPlugin(memory), "TextMemoryPlugin")
|
||||
kernel.add_plugin(sk.core_plugins.TextMemoryPlugin(memory), "TextMemoryPlugin")
|
||||
|
||||
await memory.save_information(collection="generic", id="info1", text="My budget for 2024 is $100,000")
|
||||
await memory.save_reference(
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user