Skip to content

Commit 03ed80f

Browse files
Merge remote-tracking branch 'origin/main' into feature-declarative-agents
2 parents a15fde7 + 9e979f8 commit 03ed80f

File tree

88 files changed

+5071
-6910
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+5071
-6910
lines changed

dotnet/samples/Concepts/ChatCompletion/HybridCompletion_Fallback.cs

+33-18
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22

33
using System.ClientModel;
44
using System.ClientModel.Primitives;
5-
using System.ComponentModel;
65
using System.Net;
76
using System.Runtime.CompilerServices;
87
using Azure.AI.OpenAI;
98
using Azure.Identity;
109
using Microsoft.Extensions.AI;
10+
using Microsoft.Extensions.DependencyInjection;
1111
using Microsoft.SemanticKernel;
12+
using Microsoft.SemanticKernel.ChatCompletion;
13+
using Microsoft.SemanticKernel.Connectors.AzureOpenAI;
1214

1315
namespace ChatCompletion;
1416

@@ -30,23 +32,33 @@ public class HybridCompletion_Fallback(ITestOutputHelper output) : BaseTest(outp
3032
[Fact]
3133
public async Task FallbackToAvailableModelAsync()
3234
{
33-
// Create an unavailable chat client that fails with 503 Service Unavailable HTTP status code
34-
IChatClient unavailableChatClient = CreateUnavailableOpenAIChatClient();
35+
IKernelBuilder kernelBuilder = Kernel.CreateBuilder();
3536

36-
// Create a cloud available chat client
37-
IChatClient availableChatClient = CreateAzureOpenAIChatClient();
37+
// Create and register an unavailable chat client that fails with 503 Service Unavailable HTTP status code
38+
kernelBuilder.Services.AddSingleton<IChatClient>(CreateUnavailableOpenAIChatClient());
3839

39-
// Create a fallback chat client that will fallback to the available chat client when unavailable chat client fails
40-
IChatClient fallbackChatClient = new FallbackChatClient([unavailableChatClient, availableChatClient]);
40+
// Create and register a cloud available chat client
41+
kernelBuilder.Services.AddSingleton<IChatClient>(CreateAzureOpenAIChatClient());
42+
43+
// Create and register fallback chat client that will fallback to the available chat client when unavailable chat client fails
44+
kernelBuilder.Services.AddSingleton<IChatCompletionService>((sp) =>
45+
{
46+
IEnumerable<IChatClient> chatClients = sp.GetServices<IChatClient>();
4147

42-
ChatOptions chatOptions = new() { Tools = [AIFunctionFactory.Create(GetWeather)] };
48+
return new FallbackChatClient(chatClients.ToList()).AsChatCompletionService();
49+
});
4350

44-
var result = await fallbackChatClient.GetResponseAsync("Do I need an umbrella?", chatOptions);
51+
Kernel kernel = kernelBuilder.Build();
52+
kernel.ImportPluginFromFunctions("Weather", [KernelFunctionFactory.CreateFromMethod(() => "It's sunny", "GetWeather")]);
4553

46-
Output.WriteLine(result);
54+
AzureOpenAIPromptExecutionSettings settings = new()
55+
{
56+
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
57+
};
4758

48-
[Description("Gets the weather")]
49-
string GetWeather() => "It's sunny";
59+
FunctionResult result = await kernel.InvokePromptAsync("Do I need an umbrella?", new(settings));
60+
61+
Output.WriteLine(result);
5062
}
5163

5264
/// <summary>
@@ -62,19 +74,22 @@ public async Task FallbackToAvailableModelStreamingAsync()
6274
IChatClient availableChatClient = CreateAzureOpenAIChatClient();
6375

6476
// Create a fallback chat client that will fallback to the available chat client when unavailable chat client fails
65-
IChatClient fallbackChatClient = new FallbackChatClient([unavailableChatClient, availableChatClient]);
77+
IChatCompletionService fallbackCompletionService = new FallbackChatClient([unavailableChatClient, availableChatClient]).AsChatCompletionService();
6678

67-
ChatOptions chatOptions = new() { Tools = [AIFunctionFactory.Create(GetWeather)] };
79+
Kernel kernel = new();
80+
kernel.ImportPluginFromFunctions("Weather", [KernelFunctionFactory.CreateFromMethod(() => "It's sunny", "GetWeather")]);
6881

69-
var result = fallbackChatClient.GetStreamingResponseAsync("Do I need an umbrella?", chatOptions);
82+
AzureOpenAIPromptExecutionSettings settings = new()
83+
{
84+
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
85+
};
86+
87+
IAsyncEnumerable<StreamingChatMessageContent> result = fallbackCompletionService.GetStreamingChatMessageContentsAsync("Do I need an umbrella?", settings, kernel);
7088

7189
await foreach (var update in result)
7290
{
7391
Output.WriteLine(update);
7492
}
75-
76-
[Description("Gets the weather")]
77-
string GetWeather() => "It's sunny";
7893
}
7994

8095
private static IChatClient CreateUnavailableOpenAIChatClient()

dotnet/src/Functions/Functions.OpenApi/Model/RestApiOperation.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ internal IDictionary<string, string> BuildHeaders(IDictionary<string, object?> a
170170
throw new KernelException($"The headers parameter '{parameterStyle}' serialization style is not supported.");
171171
}
172172

173-
var node = OpenApiTypeConverter.Convert(parameter.Name, parameter.Type, argument);
173+
var node = OpenApiTypeConverter.Convert(parameter.Name, parameter.Type, argument, parameter.Schema);
174174

175175
//Serializing the parameter and adding it to the headers.
176176
headers.Add(parameter.Name, serializer.Invoke(parameter, node));
@@ -206,7 +206,7 @@ internal string BuildQueryString(IDictionary<string, object?> arguments)
206206
throw new KernelException($"The query string parameter '{parameterStyle}' serialization style is not supported.");
207207
}
208208

209-
var node = OpenApiTypeConverter.Convert(parameter.Name, parameter.Type, argument);
209+
var node = OpenApiTypeConverter.Convert(parameter.Name, parameter.Type, argument, parameter.Schema);
210210

211211
// Serializing the parameter and adding it to the query string if there's an argument for it.
212212
segments.Add(serializer.Invoke(parameter, node));
@@ -274,7 +274,7 @@ private string BuildPath(string pathTemplate, IDictionary<string, object?> argum
274274
throw new KernelException($"The path parameter '{parameterStyle}' serialization style is not supported.");
275275
}
276276

277-
var node = OpenApiTypeConverter.Convert(parameter.Name, parameter.Type, argument);
277+
var node = OpenApiTypeConverter.Convert(parameter.Name, parameter.Type, argument, parameter.Schema);
278278

279279
// Serializing the parameter and adding it to the path.
280280
pathTemplate = pathTemplate.Replace($"{{{parameter.Name}}}", HttpUtility.UrlEncode(serializer.Invoke(parameter, node)));

dotnet/src/Functions/Functions.OpenApi/RestApiOperationRunner.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -427,13 +427,13 @@ private JsonObject BuildJsonObject(IList<RestApiPayloadProperty> properties, IDi
427427
// Use property argument name to look up the property value
428428
if (!string.IsNullOrEmpty(propertyMetadata.ArgumentName) && arguments.TryGetValue(propertyMetadata.ArgumentName!, out object? argument) && argument is not null)
429429
{
430-
result.Add(propertyMetadata.Name, OpenApiTypeConverter.Convert(propertyMetadata.Name, propertyMetadata.Type, argument));
430+
result.Add(propertyMetadata.Name, OpenApiTypeConverter.Convert(propertyMetadata.Name, propertyMetadata.Type, argument, propertyMetadata.Schema));
431431
continue;
432432
}
433433
// Use property name to look up the property value
434434
else if (arguments.TryGetValue(argumentName, out argument) && argument is not null)
435435
{
436-
result.Add(propertyMetadata.Name, OpenApiTypeConverter.Convert(propertyMetadata.Name, propertyMetadata.Type, argument));
436+
result.Add(propertyMetadata.Name, OpenApiTypeConverter.Convert(propertyMetadata.Name, propertyMetadata.Type, argument, propertyMetadata.Schema));
437437
continue;
438438
}
439439

dotnet/src/Functions/Functions.OpenApi/Serialization/OpenApiTypeConverter.cs

+30-5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Globalization;
55
using System.Text.Json;
66
using System.Text.Json.Nodes;
7+
using Json.Schema;
78

89
namespace Microsoft.SemanticKernel.Plugins.OpenApi;
910

@@ -13,19 +14,20 @@ namespace Microsoft.SemanticKernel.Plugins.OpenApi;
1314
internal static class OpenApiTypeConverter
1415
{
1516
/// <summary>
16-
/// Converts the given parameter argument to a JsonNode based on the specified type.
17+
/// Converts the given parameter argument to a JsonNode based on the specified type or schema.
1718
/// </summary>
1819
/// <param name="name">The parameter name.</param>
1920
/// <param name="type">The parameter type.</param>
2021
/// <param name="argument">The argument to be converted.</param>
22+
/// <param name="schema">The parameter schema.</param>
2123
/// <returns>A JsonNode representing the converted value.</returns>
22-
public static JsonNode Convert(string name, string type, object argument)
24+
public static JsonNode Convert(string name, string type, object argument, KernelJsonSchema? schema = null)
2325
{
2426
Verify.NotNull(argument);
2527

2628
try
2729
{
28-
JsonNode? converter = type switch
30+
JsonNode? node = type switch
2931
{
3032
"string" => JsonValue.Create(argument),
3133
"array" => argument switch
@@ -52,10 +54,12 @@ string stringArgument when double.TryParse(stringArgument, out var doubleValue)
5254
byte or sbyte or short or ushort or int or uint or long or ulong or float or double or decimal => JsonValue.Create(argument),
5355
_ => null
5456
},
55-
_ => throw new NotSupportedException($"Unexpected type '{type}' of parameter '{name}' with argument '{argument}'."),
57+
_ => schema is null
58+
? JsonSerializer.SerializeToNode(argument)
59+
: ValidateSchemaAndConvert(name, schema, argument)
5660
};
5761

58-
return converter ?? throw new ArgumentOutOfRangeException(name, argument, $"Argument type '{argument.GetType()}' is not convertible to parameter type '{type}'.");
62+
return node ?? throw new ArgumentOutOfRangeException(name, argument, $"Argument type '{argument.GetType()}' is not convertible to parameter type '{type}'.");
5963
}
6064
catch (ArgumentException ex)
6165
{
@@ -66,4 +70,25 @@ string stringArgument when double.TryParse(stringArgument, out var doubleValue)
6670
throw new ArgumentOutOfRangeException(name, argument, ex.Message);
6771
}
6872
}
73+
74+
/// <summary>
75+
/// Validates the argument against the parameter schema and converts it to a JsonNode if valid.
76+
/// </summary>
77+
/// <param name="parameterName">The parameter name.</param>
78+
/// <param name="parameterSchema">The parameter schema.</param>
79+
/// <param name="argument">The argument to be validated and converted.</param>
80+
/// <returns>A JsonNode representing the converted value.</returns>
81+
private static JsonNode? ValidateSchemaAndConvert(string parameterName, KernelJsonSchema parameterSchema, object argument)
82+
{
83+
var jsonSchema = JsonSchema.FromText(JsonSerializer.Serialize(parameterSchema));
84+
85+
var node = JsonSerializer.SerializeToNode(argument);
86+
87+
if (jsonSchema.Evaluate(node).IsValid)
88+
{
89+
return node;
90+
}
91+
92+
throw new ArgumentOutOfRangeException(parameterName, argument, $"Argument type '{argument.GetType()}' does not match the schema.");
93+
}
6994
}

dotnet/src/Functions/Functions.UnitTests/OpenApi/Serialization/OpenApiTypeConverterTests.cs

+47
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System;
44
using System.Collections.Generic;
55
using System.Globalization;
6+
using Microsoft.SemanticKernel;
67
using Microsoft.SemanticKernel.Plugins.OpenApi;
78
using Microsoft.VisualBasic;
89
using Xunit;
@@ -112,4 +113,50 @@ public void ItShouldConvertCollections()
112113

113114
Assert.Equal("[1,2,3]", OpenApiTypeConverter.Convert("id", "array", "[1, 2, 3]").ToJsonString());
114115
}
116+
117+
[Fact]
118+
public void ItShouldConvertWithNoTypeAndNoSchema()
119+
{
120+
// Act
121+
var result = OpenApiTypeConverter.Convert("lat", null!, 51.8985136);
122+
123+
// Assert
124+
Assert.Equal(51.8985136, result.GetValue<double>());
125+
}
126+
127+
[Fact]
128+
public void ItShouldConvertWithNoTypeAndValidSchema()
129+
{
130+
// Arrange
131+
var schema = KernelJsonSchema.Parse(
132+
"""
133+
{
134+
"type": "number",
135+
"format": "double",
136+
"nullable": false
137+
}
138+
""");
139+
140+
// Act
141+
var result = OpenApiTypeConverter.Convert("lat", null!, 51.8985136, schema);
142+
143+
// Assert
144+
Assert.Equal(51.8985136, result.GetValue<double>());
145+
}
146+
147+
[Fact]
148+
public void ItShouldThrowExceptionWhenNoTypeAndInvalidSchema()
149+
{
150+
// Arrange
151+
var schema = KernelJsonSchema.Parse(
152+
"""
153+
{
154+
"type": "boolean",
155+
"nullable": false
156+
}
157+
""");
158+
159+
// Act & Assert
160+
Assert.Throws<ArgumentOutOfRangeException>(() => OpenApiTypeConverter.Convert("lat", null!, 51.8985136, schema));
161+
}
115162
}

python/samples/concepts/README.md

+38-7
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,51 @@
44

55
### Agents - Creating and using [agents](../../semantic_kernel/agents/) in Semantic Kernel
66

7-
- [Assistant Agent Chart Maker](./agents/assistant_agent/assistant_agent_chart_maker.py)
8-
- [Assistant Agent File Manipulation](./agents/assistant_agent/assistant_agent_file_manipulation.py)
9-
- [Assistant Agent File Manipulation Streaming](./agents/assistant_agent/assistant_agent_file_manipulation_streaming.py)
10-
- [Assistant Agent Retrieval](./agents/assistant_agent/assistant_agent_retrieval.py)
11-
- [Assistant Agent Streaming](./agents/assistant_agent/assistant_agent_streaming.py)
7+
#### [OpenAI Assistant Agent](../../semantic_kernel/agents/open_ai/open_ai_assistant_agent.py)
8+
9+
- [OpenAI Assistant Chart Maker Streaming](./agents/openai_assistant/openai_assistant_chart_maker_streaming.py)
10+
- [OpenAI Assistant Chart Maker](./agents/openai_assistant/openai_assistant_chart_maker.py)
11+
- [OpenAI Assistant File Manipulation Streaming](./agents/openai_assistant/openai_assistant_file_manipulation_streaming.py)
12+
- [OpenAI Assistant File Manipulation](./agents/openai_assistant/openai_assistant_file_manipulation.py)
13+
- [OpenAI Assistant File Manipulation Streaming](./agents/openai_assistant/openai_assistant_file_manipulation_streaming.py)
14+
- [OpenAI Assistant Retrieval](./agents/openai_assistant/openai_assistant_retrieval.py)
15+
- [OpenAI Assistant Streaming](./agents/openai_assistant/openai_assistant_streaming.py)
16+
- [OpenAI Assistant Structured Outputs](./agents/openai_assistant/openai_assistant_structured_outputs.py)
17+
- [OpenAI Assistant Templating Streaming](./agents/openai_assistant/openai_assistant_templating_streaming.py)
18+
- [OpenAI Assistant Vision Streaming](./agents/openai_assistant/openai_assistant_vision_streaming.py)
19+
20+
#### [Azure AI Agent](../../semantic_kernel/agents/azure_ai/azure_ai_agent.py)
21+
22+
- [Azure AI Agent with Azure AI Search](./agents/azure_ai_agent/azure_ai_agent_azure_ai_search.py)
23+
- [Azure AI Agent File Manipulation](./agents/azure_ai_agent/azure_ai_agent_file_manipulation.py)
24+
- [Azure AI Agent Streaming](./agents/azure_ai_agent/azure_ai_agent_streaming.py)
25+
26+
#### [Bedrock Agent](../../semantic_kernel/agents/bedrock/bedrock_agent.py)
27+
- [Bedrock Agent Simple Chat Streaming](./agents/bedrock_agent/bedrock_agent_simple_chat_streaming.py)
28+
- [Bedrock Agent Simple Chat](./agents/bedrock_agent/bedrock_agent_simple_chat.py)
29+
- [Bedrock Agent Update Agent](./agents/bedrock_agent/bedrock_agent_update_agent.py)
30+
- [Bedrock Agent Use Existing](./agents/bedrock_agent/bedrock_agent_use_existing.py)
31+
- [Bedrock Agent With Code Interpreter Streaming](./agents/bedrock_agent/bedrock_agent_with_code_interpreter_streaming.py)
32+
- [Bedrock Agent With Code Interpreter](./agents/bedrock_agent/bedrock_agent_with_code_interpreter.py)
33+
- [Bedrock Agent With Kernel Function Streaming](./agents/bedrock_agent/bedrock_agent_with_kernel_function_streaming.py)
34+
- [Bedrock Agent With Kernel Function](./agents/bedrock_agent/bedrock_agent_with_kernel_function.py)
35+
- [Bedrock Agent Mixed Chat Agents Streaming](./agents/bedrock_agent/bedrock_mixed_chat_agents_streaming.py)
36+
- [Bedrock Agent Mixed Chat Agents](./agents/bedrock_agent/bedrock_mixed_chat_agents.py)
37+
38+
#### [Chat Completion Agent](../../semantic_kernel/agents/chat_completion/chat_completion_agent.py)
39+
1240
- [Chat Completion Function Termination](./agents/chat_completion_agent/chat_completion_function_termination.py)
1341
- [Chat Completion History Reducer](./agents/chat_completion_agent/chat_completion_history_reducer.py)
14-
- [Mixed Chat Agents](./agents/mixed_chat/mixed_chat_agents.py)
42+
- [Chat Completion Templating](./agents/chat_completion_agent/chat_completion_templating.py)
43+
44+
#### [Mixed Agent Group Chat](../../semantic_kernel/agents/group_chat/agent_group_chat.py)
45+
1546
- [Mixed Chat Agents Plugins](./agents/mixed_chat/mixed_chat_agents_plugins.py)
47+
- [Mixed Chat Agents](./agents/mixed_chat/mixed_chat_agents.py)
1648
- [Mixed Chat Files](./agents/mixed_chat/mixed_chat_files.py)
1749
- [Mixed Chat Images](./agents/mixed_chat/mixed_chat_images.py)
1850
- [Mixed Chat Reset](./agents/mixed_chat/mixed_chat_reset.py)
1951
- [Mixed Chat Streaming](./agents/mixed_chat/mixed_chat_streaming.py)
20-
- [Bedrock Agent](./agents/bedrock_agent/README.md)
2152

2253
### Audio - Using services that support audio-to-text and text-to-audio conversion
2354

python/samples/concepts/agents/README.md

+9-6
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ This project contains a step by step guide to get started with _Semantic Kernel
77
- For the use of Chat Completion agents, the minimum allowed Semantic Kernel pypi version is 1.3.0.
88
- For the use of OpenAI Assistant agents, the minimum allowed Semantic Kernel pypi version is 1.4.0.
99
- For the use of Agent Group Chat, the minimum allowed Semantic kernel pypi version is 1.6.0.
10-
- For the use of Streaming OpenAI Assistant agents, the minimum allowed Semantic Kernel pypi version is 1.11.0
10+
- For the use of Streaming OpenAI Assistant agents, the minimum allowed Semantic Kernel pypi version is 1.11.0.
11+
- For the use of AzureAI and Bedrock agents, the minimum allowed Semantic Kernel pypi version is 1.21.0.
12+
- For the use of Crew.AI as a plugin, the minimum allowed Semantic Kernel pypi version is 1.21.1.
13+
1114

1215
## Source
1316

@@ -20,12 +23,12 @@ The concept agents examples are grouped by prefix:
2023
Prefix|Description
2124
---|---
2225
assistant|How to use agents based on the [Open AI Assistant API](https://platform.openai.com/docs/assistants).
23-
chat_completion|How to use Semantic Kernel Chat Completion agents.
24-
bedrock|How to use AWS Bedrock agents in Semantic Kernel.
26+
autogen_conversable_agent| How to use [AutoGen 0.2 Conversable Agents](https://microsoft.github.io/autogen/0.2/docs/Getting-Started) within Semantic Kernel.
27+
azure_ai_agent|How to use an [Azure AI Agent](https://learn.microsoft.com/en-us/azure/ai-services/agents/quickstart?pivots=programming-language-python-azure) within Semantic Kernel.
28+
chat_completion_agent|How to use Semantic Kernel Chat Completion agents that leverage AI Connector Chat Completion APIs.
29+
bedrock|How to use [AWS Bedrock agents](https://aws.amazon.com/bedrock/agents/) in Semantic Kernel.
2530
mixed_chat|How to combine different agent types.
26-
complex_chat|**Coming Soon**
27-
28-
_Note: As we strive for parity with .NET, more getting_started_with_agent samples will be added. The current steps and names may be revised to further align with our .NET counterpart._
31+
openai_assistant|How to use [OpenAI Assistants](https://platform.openai.com/docs/assistants/overview) in Semantic Kernel.
2932

3033
## Configuring the Kernel
3134

python/samples/concepts/agents/assistant_agent/README.md

-19
This file was deleted.

0 commit comments

Comments
 (0)