Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions dotnet/samples/Demos/TelemetryWithAppInsights/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.AzureAIInference;
using Microsoft.SemanticKernel.Connectors.Google;
using Microsoft.SemanticKernel.Connectors.HuggingFace;
Expand Down Expand Up @@ -102,6 +104,13 @@ public static async Task Main()
await RunAzureOpenAIToolCallsAsync(kernel);
Console.WriteLine();
}

Console.WriteLine("Run ChatCompletion Agent.");
using (var _ = s_activitySource.StartActivity("Agent"))
{
await RunChatCompletionAgentAsync(kernel);
Console.WriteLine();
}
}

#region Private
Expand Down Expand Up @@ -307,6 +316,40 @@ private static async Task RunAutoToolCallAsync(Kernel kernel)
}
#endregion

#region Agent

private static async Task RunChatCompletionAgentAsync(Kernel kernel)
{
Console.WriteLine("============= ChatCompletion Agent =============");

if (TestConfiguration.AzureOpenAI is null)
{
Console.WriteLine("Azure OpenAI is not configured. Skipping.");
return;
}

SetTargetService(kernel, AzureOpenAIServiceKey);

// Define the agent
ChatCompletionAgent agent =
new()
{
Name = "TestAgent",
Instructions = "You are a helpful assistant.",
Kernel = kernel
};

ChatMessageContent message = new(AuthorRole.User, "Write a poem about John Doe.");
Console.WriteLine($"User: {message.Content}");

await foreach (AgentResponseItem<ChatMessageContent> response in agent.InvokeAsync(message))
{
Console.WriteLine($"Agent: {response.Message.Content}");
}
}

#endregion

private static Kernel GetKernel(ILoggerFactory loggerFactory)
{
var folder = RepoFiles.SamplePluginsPath();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<ProjectReference Include="..\..\..\src\Connectors\Connectors.Google\Connectors.Google.csproj" />
<ProjectReference Include="..\..\..\src\Connectors\Connectors.HuggingFace\Connectors.HuggingFace.csproj" />
<ProjectReference Include="..\..\..\src\SemanticKernel.Core\SemanticKernel.Core.csproj" />
<ProjectReference Include="..\..\..\src\Agents\Core\Agents.Core.csproj" />
<ProjectReference Include="..\..\..\src\Plugins\Plugins.Core\Plugins.Core.csproj" />
<ProjectReference Include="..\..\..\src\Plugins\Plugins.Web\Plugins.Web.csproj" />
</ItemGroup>
Expand Down
47 changes: 25 additions & 22 deletions dotnet/src/Agents/AzureAI/AzureAIAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,16 @@ public async IAsyncEnumerable<AgentResponseItem<ChatMessageContent>> InvokeAsync
new AzureAIAgentInvokeOptions() { AdditionalInstructions = mergedAdditionalInstructions } :
new AzureAIAgentInvokeOptions(options) { AdditionalInstructions = mergedAdditionalInstructions };

var invokeResults = ActivityExtensions.RunWithActivityAsync(
() => ModelDiagnostics.StartAgentInvocationActivity(this.Id, this.GetDisplayName(), this.Description),
() => InternalInvokeAsync(),
cancellationToken);
using var activity = ModelDiagnostics.StartAgentInvocationActivity(this.Id, this.GetDisplayName(), this.Description, messages);
List<ChatMessageContent>? chatMessageContents = activity is not null ? [] : null;

await foreach (var result in InternalInvokeAsync().ConfigureAwait(false))
{
yield return new(result, azureAIAgentThread);
chatMessageContents?.Add(result);
}

activity?.SetAgentResponse(chatMessageContents);

async IAsyncEnumerable<ChatMessageContent> InternalInvokeAsync()
{
Expand All @@ -191,12 +197,6 @@ async IAsyncEnumerable<ChatMessageContent> InternalInvokeAsync()
}
}
}

// Notify the thread of new messages and return them to the caller.
await foreach (var result in invokeResults.ConfigureAwait(false))
{
yield return new(result, azureAIAgentThread);
}
}

/// <inheritdoc/>
Expand Down Expand Up @@ -264,21 +264,21 @@ public async IAsyncEnumerable<AgentResponseItem<StreamingChatMessageContent>> In
new AzureAIAgentInvokeOptions() { AdditionalInstructions = mergedAdditionalInstructions } :
new AzureAIAgentInvokeOptions(options) { AdditionalInstructions = mergedAdditionalInstructions };

using var activity = ModelDiagnostics.StartAgentInvocationActivity(this.Id, this.GetDisplayName(), this.Description, messages);
List<StreamingChatMessageContent>? streamedContents = activity is not null ? [] : null;

// Invoke the Agent with the thread that we already added our message to, and with
// a chat history to receive complete messages.
ChatHistory newMessagesReceiver = [];
var invokeResults = ActivityExtensions.RunWithActivityAsync(
() => ModelDiagnostics.StartAgentInvocationActivity(this.Id, this.GetDisplayName(), this.Description),
() => AgentThreadActions.InvokeStreamingAsync(
this,
this.Client,
azureAIAgentThread.Id!,
newMessagesReceiver,
extensionsContextOptions.ToAzureAIInvocationOptions(),
this.Logger,
kernel,
options?.KernelArguments,
cancellationToken),
var invokeResults = AgentThreadActions.InvokeStreamingAsync(
this,
this.Client,
azureAIAgentThread.Id!,
newMessagesReceiver,
extensionsContextOptions.ToAzureAIInvocationOptions(),
this.Logger,
kernel,
options?.KernelArguments,
cancellationToken);

// Return the chunks to the caller.
Expand All @@ -289,11 +289,14 @@ public async IAsyncEnumerable<AgentResponseItem<StreamingChatMessageContent>> In
await NotifyMessagesAsync().ConfigureAwait(false);

yield return new(result, azureAIAgentThread);
streamedContents?.Add(result);
}

// Notify the thread of any remaining messages that were assembled from the streaming response after all iterations are complete.
await NotifyMessagesAsync().ConfigureAwait(false);

activity?.EndAgentStreamingResponse(streamedContents);

async Task NotifyMessagesAsync()
{
for (; messageIndex < newMessagesReceiver.Count; messageIndex++)
Expand Down
4 changes: 2 additions & 2 deletions dotnet/src/Agents/AzureAI/AzureAIChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ protected override async Task ReceiveAsync(IEnumerable<ChatMessageContent> histo
CancellationToken cancellationToken)
{
return ActivityExtensions.RunWithActivityAsync(
() => ModelDiagnostics.StartAgentInvocationActivity(agent.Id, agent.GetDisplayName(), agent.Description),
() => ModelDiagnostics.StartAgentInvocationActivity(agent.Id, agent.GetDisplayName(), agent.Description, []),
() => AgentThreadActions.InvokeAsync(agent, client, threadId, invocationOptions: null, this.Logger, agent.Kernel, agent.Arguments, cancellationToken),
cancellationToken);
}
Expand All @@ -54,7 +54,7 @@ protected override async Task ReceiveAsync(IEnumerable<ChatMessageContent> histo
protected override IAsyncEnumerable<StreamingChatMessageContent> InvokeStreamingAsync(AzureAIAgent agent, IList<ChatMessageContent> messages, CancellationToken cancellationToken = default)
{
return ActivityExtensions.RunWithActivityAsync(
() => ModelDiagnostics.StartAgentInvocationActivity(agent.Id, agent.GetDisplayName(), agent.Description),
() => ModelDiagnostics.StartAgentInvocationActivity(agent.Id, agent.GetDisplayName(), agent.Description, messages),
() => AgentThreadActions.InvokeStreamingAsync(agent, client, threadId, messages, invocationOptions: null, this.Logger, agent.Kernel, agent.Arguments, cancellationToken),
cancellationToken);
}
Expand Down
35 changes: 26 additions & 9 deletions dotnet/src/Agents/Bedrock/BedrockAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ public override async IAsyncEnumerable<AgentResponseItem<ChatMessageContent>> In
AgentInvokeOptions? options = null,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
Verify.NotNull(messages, nameof(messages));
if (messages.Count == 0)
{
throw new InvalidOperationException("The Bedrock agent requires a message to be invoked.");
Expand Down Expand Up @@ -145,15 +144,21 @@ public override async IAsyncEnumerable<AgentResponseItem<ChatMessageContent>> In
};
});

using var activity = ModelDiagnostics.StartAgentInvocationActivity(this.Id, this.GetDisplayName(), this.Description, messages);

// Invoke the agent
var invokeResults = this.InvokeInternalAsync(invokeAgentRequest, options?.KernelArguments, cancellationToken);
List<ChatMessageContent>? chatMessageContents = activity is not null ? [] : null;

// Return the results to the caller in AgentResponseItems.
await foreach (var result in invokeResults.ConfigureAwait(false))
{
await this.NotifyThreadOfNewMessage(bedrockThread, result, cancellationToken).ConfigureAwait(false);
yield return new(result, bedrockThread);
chatMessageContents?.Add(result);
}

activity?.SetAgentResponse(chatMessageContents);
}

/// <summary>
Expand Down Expand Up @@ -206,6 +211,9 @@ public async IAsyncEnumerable<AgentResponseItem<ChatMessageContent>> InvokeAsync
invokeAgentRequest.SessionId = bedrockThread.Id;
invokeAgentRequest = this.ConfigureAgentRequest(options, () => invokeAgentRequest);

using var activity = ModelDiagnostics.StartAgentInvocationActivity(this.Id, this.GetDisplayName(), this.Description, []);
List<ChatMessageContent>? chatMessageContents = activity is not null ? [] : null;

// Invoke the agent
var invokeResults = this.InvokeInternalAsync(invokeAgentRequest, options?.KernelArguments, cancellationToken);

Expand All @@ -214,7 +222,10 @@ public async IAsyncEnumerable<AgentResponseItem<ChatMessageContent>> InvokeAsync
{
await this.NotifyThreadOfNewMessage(bedrockThread, result, cancellationToken).ConfigureAwait(false);
yield return new(result, bedrockThread);
chatMessageContents?.Add(result);
}

activity?.SetAgentResponse(chatMessageContents);
}

#endregion
Expand Down Expand Up @@ -286,14 +297,20 @@ public override async IAsyncEnumerable<AgentResponseItem<StreamingChatMessageCon
};
});

using var activity = ModelDiagnostics.StartAgentInvocationActivity(this.Id, this.GetDisplayName(), this.Description, []);
List<StreamingChatMessageContent>? streamedContents = activity is not null ? [] : null;

// Invoke the agent
var invokeResults = this.InvokeStreamingInternalAsync(invokeAgentRequest, bedrockThread, options?.KernelArguments, cancellationToken);

// Return the results to the caller in AgentResponseItems.
await foreach (var result in invokeResults.ConfigureAwait(false))
{
yield return new(result, bedrockThread);
streamedContents?.Add(result);
}

activity?.EndAgentStreamingResponse(streamedContents);
}

/// <summary>
Expand Down Expand Up @@ -348,6 +365,9 @@ public async IAsyncEnumerable<AgentResponseItem<StreamingChatMessageContent>> In
invokeAgentRequest.SessionId = bedrockThread.Id;
invokeAgentRequest = this.ConfigureAgentRequest(options, () => invokeAgentRequest);

using var activity = ModelDiagnostics.StartAgentInvocationActivity(this.Id, this.GetDisplayName(), this.Description, []);
List<StreamingChatMessageContent>? streamedContents = activity is not null ? [] : null;

var invokeResults = this.InvokeStreamingInternalAsync(invokeAgentRequest, bedrockThread, options?.KernelArguments, cancellationToken);

// The Bedrock agent service has the same API for both streaming and non-streaming responses.
Expand All @@ -364,7 +384,10 @@ public async IAsyncEnumerable<AgentResponseItem<StreamingChatMessageContent>> In
Metadata = chatMessageContent.Metadata,
},
thread: bedrockThread);
streamedContents?.Add(chatMessageContent);
}

activity?.EndAgentStreamingResponse(streamedContents);
}

#endregion
Expand Down Expand Up @@ -409,10 +432,7 @@ private IAsyncEnumerable<ChatMessageContent> InvokeInternalAsync(
{
return invokeAgentRequest.StreamingConfigurations != null && (invokeAgentRequest.StreamingConfigurations.StreamFinalResponse ?? false)
? throw new ArgumentException("The streaming configuration must be null for non-streaming responses.")
: ActivityExtensions.RunWithActivityAsync(
() => ModelDiagnostics.StartAgentInvocationActivity(this.Id, this.GetDisplayName(), this.Description),
InvokeInternal,
cancellationToken);
: InvokeInternal();

// Collect all responses from the agent and return them as a single chat message content since this
// is a non-streaming API.
Expand Down Expand Up @@ -471,10 +491,7 @@ private IAsyncEnumerable<StreamingChatMessageContent> InvokeStreamingInternalAsy
throw new ArgumentException("The streaming configuration must have StreamFinalResponse set to true.");
}

return ActivityExtensions.RunWithActivityAsync(
() => ModelDiagnostics.StartAgentInvocationActivity(this.Id, this.GetDisplayName(), this.Description),
InvokeInternal,
cancellationToken);
return InvokeInternal();

async IAsyncEnumerable<StreamingChatMessageContent> InvokeInternal()
{
Expand Down
32 changes: 18 additions & 14 deletions dotnet/src/Agents/Core/ChatCompletionAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,7 @@ protected internal override IAsyncEnumerable<ChatMessageContent> InvokeAsync(
{
string agentName = this.GetDisplayName();

return ActivityExtensions.RunWithActivityAsync(
() => ModelDiagnostics.StartAgentInvocationActivity(this.Id, agentName, this.Description),
() => this.InternalInvokeAsync(agentName, history, (m) => Task.CompletedTask, arguments, kernel, null, cancellationToken),
cancellationToken);
return this.InternalInvokeAsync(agentName, history, (m) => Task.CompletedTask, arguments, kernel, null, cancellationToken);
}

/// <inheritdoc/>
Expand Down Expand Up @@ -241,16 +238,13 @@ protected internal override IAsyncEnumerable<StreamingChatMessageContent> Invoke
{
string agentName = this.GetDisplayName();

return ActivityExtensions.RunWithActivityAsync(
() => ModelDiagnostics.StartAgentInvocationActivity(this.Id, agentName, this.Description),
() => this.InternalInvokeStreamingAsync(
agentName,
history,
(newMessage) => Task.CompletedTask,
arguments,
kernel,
null,
cancellationToken),
return this.InternalInvokeStreamingAsync(
agentName,
history,
(newMessage) => Task.CompletedTask,
arguments,
kernel,
null,
cancellationToken);
}

Expand Down Expand Up @@ -355,6 +349,8 @@ private async IAsyncEnumerable<ChatMessageContent> InternalInvokeAsync(

this.Logger.LogAgentChatServiceInvokingAgent(nameof(InvokeAsync), this.Id, agentName, serviceType);

using var activity = ModelDiagnostics.StartAgentInvocationActivity(this.Id, agentName, this.Description, chat);

IReadOnlyList<ChatMessageContent> messages =
await chatCompletionService.GetChatMessageContentsAsync(
chat,
Expand All @@ -381,6 +377,8 @@ await chatCompletionService.GetChatMessageContentsAsync(

yield return message;
}

activity?.SetAgentResponse(messages);
}

private async IAsyncEnumerable<StreamingChatMessageContent> InternalInvokeStreamingAsync(
Expand All @@ -404,6 +402,8 @@ private async IAsyncEnumerable<StreamingChatMessageContent> InternalInvokeStream

this.Logger.LogAgentChatServiceInvokingAgent(nameof(InvokeAsync), this.Id, agentName, serviceType);

using var activity = ModelDiagnostics.StartAgentInvocationActivity(this.Id, agentName, this.Description, chat);

IAsyncEnumerable<StreamingChatMessageContent> messages =
chatCompletionService.GetStreamingChatMessageContentsAsync(
chat,
Expand All @@ -416,6 +416,7 @@ private async IAsyncEnumerable<StreamingChatMessageContent> InternalInvokeStream
int messageIndex = messageCount;
AuthorRole? role = null;
StringBuilder builder = new();
List<StreamingChatMessageContent>? streamedContents = activity is not null ? [] : null;
await foreach (StreamingChatMessageContent message in messages.ConfigureAwait(false))
{
role = message.Role;
Expand All @@ -435,6 +436,7 @@ private async IAsyncEnumerable<StreamingChatMessageContent> InternalInvokeStream
history.Add(chatMessage);
}

streamedContents?.Add(message);
yield return message;
}

Expand All @@ -444,6 +446,8 @@ private async IAsyncEnumerable<StreamingChatMessageContent> InternalInvokeStream
await onNewMessage(new(role ?? AuthorRole.Assistant, builder.ToString()) { AuthorName = this.Name }).ConfigureAwait(false);
history.Add(new(role ?? AuthorRole.Assistant, builder.ToString()) { AuthorName = this.Name });
}

activity?.EndAgentStreamingResponse(streamedContents);
}

#endregion
Expand Down
Loading
Loading