Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decouple .NET connector from Semantic Kernel #211

Merged
merged 2 commits into from
Nov 5, 2024
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
78 changes: 78 additions & 0 deletions examples/dotnet/dotnet-03-simple-chatbot/ConnectorExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Text;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticWorkbench.Connector;

namespace AgentExample;

public static class ConnectorExtensions
{
// TODO: the list of participants is incomplete, because agents see only participants being added
public static string GetParticipantName(this Conversation conversation, string id)
{
if (conversation.Participants.TryGetValue(id, out Participant? participant))
{
return participant.Name;
}

return "Unknown";
}

public static ChatHistory ToSemanticKernelChatHistory(
this Conversation conversation,
string assistantId,
string systemPrompt)
{
var result = new ChatHistory(systemPrompt);

foreach (Message msg in conversation.Messages)
{
if (msg.Sender.Id == assistantId)
{
result.AddAssistantMessage(msg.Content!);
}
else
{
result.AddUserMessage(
$"[{conversation.GetParticipantName(msg.Sender.Id)}] {msg.Content}");
}
}

return result;
}

public static string ToHtmlString(
this Conversation conversation,
string assistantId)
{
var result = new StringBuilder();
result.AppendLine("<style>");
result.AppendLine("DIV.conversationHistory { padding: 0 20px 60px 20px; }");
result.AppendLine("DIV.conversationHistory P { margin: 0 0 8px 0; }");
result.AppendLine("</style>");
result.AppendLine("<div class='conversationHistory'>");

foreach (var msg in conversation.Messages)
{
result.AppendLine("<p>");
if (msg.Sender.Id == assistantId)
{
result.AppendLine("<b>Assistant</b><br/>");
}
else
{
result
.Append("<b>")
.Append(conversation.GetParticipantName(msg.Sender.Id))
.AppendLine("</b><br/>");
}

result.AppendLine(msg.Content).AppendLine("</p>");
}

result.Append("</div>");

return result.ToString();
}
}
2 changes: 1 addition & 1 deletion examples/dotnet/dotnet-03-simple-chatbot/MyAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ private async Task<Message> PrepareAnswerAsync(Conversation conversation, Messag

if (inputIsSafe)
{
var chatHistory = conversation.ToChatHistory(this.Id, this.Config.RenderSystemPrompt());
var chatHistory = conversation.ToSemanticKernelChatHistory(this.Id, this.Config.RenderSystemPrompt());
debugInfo.Add("lastChatMsg", chatHistory.Last().Content);

// Show chat history in workbench side panel
Expand Down
2 changes: 1 addition & 1 deletion examples/dotnet/dotnet-03-simple-chatbot/MyAgentConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace AgentExample;

public class MyAgentConfig : AgentConfig
public class MyAgentConfig : AgentConfigBase
{
// Define safety and behavioral guardrails.
// See https://learn.microsoft.com/azure/ai-services/openai/concepts/system-message for more information and examples.
Expand Down
2 changes: 1 addition & 1 deletion libraries/dotnet/WorkbenchConnector/AgentBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
namespace Microsoft.SemanticWorkbench.Connector;

public abstract class AgentBase<TAgentConfig> : IAgentBase
where TAgentConfig : IAgentConfig, new()
where TAgentConfig : AgentConfigBase, new()
{
// Agent instance ID
public string Id { get; protected set; } = string.Empty;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Collections.Generic;
using System.Reflection;

// ReSharper disable once CheckNamespace
namespace Microsoft.SemanticWorkbench.Connector;

public class AgentConfig : IAgentConfig
public abstract class AgentConfigBase
{
public AgentConfig()
{
}

public object? ToWorkbenchFormat()
public object ToWorkbenchFormat()
{
Dictionary<string, object> result = new();
Dictionary<string, object> defs = new();
Expand Down Expand Up @@ -60,22 +55,4 @@ public AgentConfig()

return result;
}

public void Update(object? config)
{
if (config == null)
{
throw new ArgumentException("Empty configuration");
}

if (config is not AgentConfig cfg)
{
throw new ArgumentException("Incompatible configuration type");
}

foreach (var property in this.GetType().GetProperties())
{
property.SetValue(this, property.GetValue(cfg));
}
}
}
10 changes: 0 additions & 10 deletions libraries/dotnet/WorkbenchConnector/AgentConfig/IAgentConfig.cs

This file was deleted.

22 changes: 0 additions & 22 deletions libraries/dotnet/WorkbenchConnector/Models/ChatHistoryExt.cs

This file was deleted.

64 changes: 0 additions & 64 deletions libraries/dotnet/WorkbenchConnector/Models/Conversation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using Microsoft.SemanticKernel.ChatCompletion;

// ReSharper disable once CheckNamespace
namespace Microsoft.SemanticWorkbench.Connector;
Expand Down Expand Up @@ -60,66 +58,4 @@ public void RemoveMessage(Message? msg)

this.Messages = this.Messages.Where(x => x.Id != msg.Id).ToList();
}

public ChatHistory ToChatHistory(string assistantId, string systemPrompt)
{
var result = new ChatHistory(systemPrompt);

foreach (Message msg in this.Messages)
{
if (msg.Sender.Id == assistantId)
{
result.AddAssistantMessage(msg.Content!);
}
else
{
result.AddUserMessage($"[{this.GetParticipantName(msg.Sender.Id)}] {msg.Content}");
}
}

return result;
}

public string ToHtmlString(string assistantId)
{
var result = new StringBuilder();
result.AppendLine("<style>");
result.AppendLine("DIV.conversationHistory { padding: 0 20px 60px 20px; }");
result.AppendLine("DIV.conversationHistory P { margin: 0 0 8px 0; }");
result.AppendLine("</style>");
result.AppendLine("<div class='conversationHistory'>");

foreach (var msg in this.Messages)
{
result.AppendLine("<p>");
if (msg.Sender.Id == assistantId)
{
result.AppendLine("<b>Assistant</b><br/>");
}
else
{
result
.Append("<b>")
.Append(this.GetParticipantName(msg.Sender.Id))
.AppendLine("</b><br/>");
}

result.AppendLine(msg.Content).AppendLine("</p>");
}

result.Append("</div>");

return result.ToString();
}

// TODO: the list of participants is incomplete, because agents see only participants being added
private string GetParticipantName(string id)
{
if (this.Participants.TryGetValue(id, out Participant? participant))
{
return participant.Name;
}

return "Unknown";
}
}
6 changes: 2 additions & 4 deletions libraries/dotnet/WorkbenchConnector/Models/ServiceInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@
namespace Microsoft.SemanticWorkbench.Connector;

public class ServiceInfo<TAgentConfig>(TAgentConfig cfg)
where TAgentConfig : IAgentConfig, new()
where TAgentConfig : AgentConfigBase, new()
{
private readonly TAgentConfig _cfg = cfg;

[JsonPropertyName("assistant_service_id")]
public string ServiceId { get; set; } = string.Empty;

Expand All @@ -24,5 +22,5 @@ public class ServiceInfo<TAgentConfig>(TAgentConfig cfg)
public Dictionary<string, object> Metadata { get; set; } = new();

[JsonPropertyName("default_config")]
public object DefaultConfiguration => this._cfg.ToWorkbenchFormat() ?? new();
public object DefaultConfiguration => cfg.ToWorkbenchFormat() ?? new();
}
24 changes: 12 additions & 12 deletions libraries/dotnet/WorkbenchConnector/Webservice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ private sealed class SemanticWorkbenchWebservice

public static WorkbenchConnector<TAgentConfig> UseAgentWebservice<TAgentConfig>(
this IEndpointRouteBuilder builder, string endpoint, bool enableCatchAll = false)
where TAgentConfig : IAgentConfig, new()
where TAgentConfig : AgentConfigBase, new()
{
WorkbenchConnector<TAgentConfig>? workbenchConnector = builder.ServiceProvider.GetService<WorkbenchConnector<TAgentConfig>>();
if (workbenchConnector == null)
Expand Down Expand Up @@ -60,7 +60,7 @@ public static WorkbenchConnector<TAgentConfig> UseAgentWebservice<TAgentConfig>(
// Return service details and default agent configuration
public static IEndpointRouteBuilder UseFetchServiceInfo<TAgentConfig>(
this IEndpointRouteBuilder builder, string prefix)
where TAgentConfig : IAgentConfig, new()
where TAgentConfig : AgentConfigBase, new()
{
builder.MapGet(prefix + "/", (
[FromServicesAttribute] WorkbenchConnector<TAgentConfig> workbenchConnector,
Expand All @@ -76,7 +76,7 @@ public static IEndpointRouteBuilder UseFetchServiceInfo<TAgentConfig>(
// Create new agent instance
public static IEndpointRouteBuilder UseCreateAgentEndpoint<TAgentConfig>(
this IEndpointRouteBuilder builder, string prefix)
where TAgentConfig : IAgentConfig, new()
where TAgentConfig : AgentConfigBase, new()
{
builder.MapPut(prefix + "/{agentId}",
async (
Expand Down Expand Up @@ -110,7 +110,7 @@ await workbenchConnector.CreateAgentAsync(agentId, name, null, cancellationToken
// Delete agent instance
public static IEndpointRouteBuilder UseDeleteAgentEndpoint<TAgentConfig>(
this IEndpointRouteBuilder builder, string prefix)
where TAgentConfig : IAgentConfig, new()
where TAgentConfig : AgentConfigBase, new()
{
builder.MapDelete(prefix + "/{agentId}",
async (
Expand All @@ -130,7 +130,7 @@ public static IEndpointRouteBuilder UseDeleteAgentEndpoint<TAgentConfig>(
// Fetch agent configuration
public static IEndpointRouteBuilder UseFetchAgentConfigEndpoint<TAgentConfig>(
this IEndpointRouteBuilder builder, string prefix)
where TAgentConfig : IAgentConfig, new()
where TAgentConfig : AgentConfigBase, new()
{
builder.MapGet(prefix + "/{agentId}/config",
(
Expand All @@ -155,7 +155,7 @@ public static IEndpointRouteBuilder UseFetchAgentConfigEndpoint<TAgentConfig>(
// Save agent configuration
public static IEndpointRouteBuilder UseSaveAgentConfigEndpoint<TAgentConfig>(
this IEndpointRouteBuilder builder, string prefix)
where TAgentConfig : IAgentConfig, new()
where TAgentConfig : AgentConfigBase, new()
{
builder.MapPut(prefix + "/{agentId}/config",
async (
Expand All @@ -171,7 +171,7 @@ public static IEndpointRouteBuilder UseSaveAgentConfigEndpoint<TAgentConfig>(
if (agent == null) { return Results.NotFound("Agent Not Found"); }

var config = agent.ParseConfig(data["config"]);
IAgentConfig newConfig =
AgentConfigBase newConfig =
await agent.UpdateAgentConfigAsync(config, cancellationToken).ConfigureAwait(false);

var tmp = workbenchConnector.GetAgent(agentId);
Expand All @@ -186,7 +186,7 @@ public static IEndpointRouteBuilder UseSaveAgentConfigEndpoint<TAgentConfig>(
// Create new conversation
private static IEndpointRouteBuilder UseCreateConversationEndpoint<TAgentConfig>(
this IEndpointRouteBuilder builder, string prefix)
where TAgentConfig : IAgentConfig, new()
where TAgentConfig : AgentConfigBase, new()
{
builder.MapPut(prefix + "/{agentId}/conversations/{conversationId}",
async (
Expand Down Expand Up @@ -214,7 +214,7 @@ private static IEndpointRouteBuilder UseCreateConversationEndpoint<TAgentConfig>
// Fetch conversation states
public static IEndpointRouteBuilder UseFetchConversationStatesEndpoint<TAgentConfig>(
this IEndpointRouteBuilder builder, string prefix)
where TAgentConfig : IAgentConfig, new()
where TAgentConfig : AgentConfigBase, new()
{
builder.MapGet(prefix + "/{agentId}/conversations/{conversationId}/states",
async (
Expand Down Expand Up @@ -285,7 +285,7 @@ end of content
// Fetch conversation states
public static IEndpointRouteBuilder UseFetchConversationInsightEndpoint<TAgentConfig>(
this IEndpointRouteBuilder builder, string prefix)
where TAgentConfig : IAgentConfig, new()
where TAgentConfig : AgentConfigBase, new()
{
builder.MapGet(prefix + "/{agentId}/conversations/{conversationId}/states/{insightId}",
async (
Expand Down Expand Up @@ -348,7 +348,7 @@ public static IEndpointRouteBuilder UseFetchConversationInsightEndpoint<TAgentCo
// New conversation event
private static IEndpointRouteBuilder UseCreateConversationEventEndpoint<TAgentConfig>(
this IEndpointRouteBuilder builder, string prefix)
where TAgentConfig : IAgentConfig, new()
where TAgentConfig : AgentConfigBase, new()
{
builder.MapPost(prefix + "/{agentId}/conversations/{conversationId}/events",
async (
Expand Down Expand Up @@ -539,7 +539,7 @@ private static IEndpointRouteBuilder UseCreateConversationEventEndpoint<TAgentCo
// Delete conversation
public static IEndpointRouteBuilder UseDeleteConversationEndpoint<TAgentConfig>(
this IEndpointRouteBuilder builder, string prefix)
where TAgentConfig : IAgentConfig, new()
where TAgentConfig : AgentConfigBase, new()
{
builder.MapDelete(prefix + "/{agentId}/conversations/{conversationId}",
async (
Expand Down
2 changes: 1 addition & 1 deletion libraries/dotnet/WorkbenchConnector/WorkbenchConnector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
namespace Microsoft.SemanticWorkbench.Connector;

public abstract class WorkbenchConnector<TAgentConfig> : IDisposable
where TAgentConfig : IAgentConfig, new()
where TAgentConfig : AgentConfigBase, new()
{
protected IAgentServiceStorage Storage { get; private set; }
protected WorkbenchConfig WorkbenchConfig { get; private set; }
Expand Down
Loading