Skip to content

Commit

Permalink
Support for nuget package publishing (#39)
Browse files Browse the repository at this point in the history
Add nuget details
Improve code docs
Improve coding standard
Add README for .NET connector
  • Loading branch information
dluc authored Sep 11, 2024
1 parent f7dae92 commit 5c3c3c0
Show file tree
Hide file tree
Showing 19 changed files with 279 additions and 74 deletions.
38 changes: 36 additions & 2 deletions assistant-connector/dotnet/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,37 @@
This project contains a .NET connector allowing to connect .NET agents and assistants to Semantic Workbench.
# Semantic Workbench

The repository contains some [examples](../../examples/) using this connector.
Semantic Workbench is a versatile tool designed for quickly prototyping intelligent assistants.
Whether you're building new assistants or integrating existing ones, the workbench offers a unified
interface for managing conversations, configuring settings, and customizing behavior.

# Connector

The Connector allows to seamlessly integrate .NET agents, built with any framework, into Semantic
Workbench. By using HTTP for communication, the connector enables your agent to handle instructions
and exchange data with both the frontend and backend of Semantic Workbench.

# Setup Guide

To integrate your agent:

1. Add the `Microsoft.SemanticWorkbench.Connector` nuget to the .NET project containing your agent.

2. **Define an agent configuration**: Create a configuration class for your agent. This can be empty
if no configuration is needed from the workbench UI.

3. **Extend Agent Functionality**: Inherit from `Microsoft.SemanticWorkbench.Connector.AgentBase`
and implement the `GetDefaultConfig` and `ParseConfig` methods in your agent class. Examples
are available in the repository.

4. **Create a Connector**: Implement `Microsoft.SemanticWorkbench.Connector.WorkbenchConnector` and
its `CreateAgentAsync` method to allow the workbench to create multiple agent instances.

5. Start a `Microsoft.SemanticWorkbench.Connector.WorkbenchConnector` calling the `ConnectAsync`
method.

6. Start a Web service using the endpoints defined in `Microsoft.SemanticWorkbench.Connector.Webservice`.

# Examples

Find sample .NET agents and assistants using this connector in the
[official repository](https://github.com/microsoft/semanticworkbench/tree/main/examples).
25 changes: 15 additions & 10 deletions assistant-connector/dotnet/WorkbenchConnector/AgentBase.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;

namespace Microsoft.SemanticWorkbench.Connector;
Expand All @@ -16,21 +20,21 @@ public abstract class AgentBase
public IAgentConfig RawConfig { get; protected set; }

// Simple storage layer to persist agents data
protected readonly IAgentServiceStorage Storage;
protected IAgentServiceStorage Storage { get; private set; }

// Reference to agent service
protected readonly WorkbenchConnector WorkbenchConnector;
protected WorkbenchConnector WorkbenchConnector { get; private set; }

// Agent logger
protected readonly ILogger Log;
protected ILogger Log { get; private set; }

/// <summary>
/// Agent instantiation
/// </summary>
/// <param name="workbenchConnector">Semantic Workbench connector</param>
/// <param name="storage">Agent data storage</param>
/// <param name="log">Agent logger</param>
public AgentBase(
protected AgentBase(
WorkbenchConnector workbenchConnector,
IAgentServiceStorage storage,
ILogger log)
Expand Down Expand Up @@ -68,6 +72,7 @@ public virtual AgentInfo ToDataModel()
/// <summary>
/// Start the agent
/// </summary>
/// <param name="cancellationToken">Async task cancellation token</param>
public virtual Task StartAsync(
CancellationToken cancellationToken = default)
{
Expand All @@ -77,6 +82,7 @@ public virtual Task StartAsync(
/// <summary>
/// Stop the agent
/// </summary>
/// <param name="cancellationToken">Async task cancellation token</param>
public virtual Task StopAsync(
CancellationToken cancellationToken = default)
{
Expand Down Expand Up @@ -158,7 +164,6 @@ await Task.WhenAll([
/// <summary>
/// Delete a conversation
/// </summary>
/// <param name="agentId">Agent instance ID</param>
/// <param name="conversationId">Conversation ID</param>
/// <param name="cancellationToken">Async task cancellation token</param>
public virtual Task DeleteConversationAsync(
Expand Down Expand Up @@ -211,11 +216,11 @@ public virtual async Task AddParticipantAsync(
/// Remove a participant from a conversation
/// </summary>
/// <param name="conversationId">Conversation ID</param>
/// <param name="participantCreatedEvent">Participant information</param>
/// <param name="participant">Participant information</param>
/// <param name="cancellationToken">Async task cancellation token</param>
public virtual async Task RemoveParticipantAsync(
string conversationId,
Participant participantUpdatedEvent,
Participant participant,
CancellationToken cancellationToken = default)
{
this.Log.LogDebug("Removing participant from conversation '{0}' on agent '{1}'",
Expand All @@ -224,7 +229,7 @@ public virtual async Task RemoveParticipantAsync(
Conversation? conversation = await this.Storage.GetConversationAsync(conversationId, this.Id, cancellationToken).ConfigureAwait(false);
if (conversation == null) { return; }

conversation.RemoveParticipant(participantUpdatedEvent);
conversation.RemoveParticipant(participant);
await this.Storage.SaveConversationAsync(conversation, cancellationToken).ConfigureAwait(false);
}

Expand Down Expand Up @@ -287,7 +292,7 @@ public virtual Task ReceiveNoteAsync(
/// Receive a command, a special type of message
/// </summary>
/// <param name="conversationId">Conversation ID</param>
/// <param name="message">Message information</param>
/// <param name="command">Command information</param>
/// <param name="cancellationToken">Async task cancellation token</param>
public virtual Task ReceiveCommandAsync(
string conversationId,
Expand Down Expand Up @@ -321,7 +326,7 @@ public virtual Task ReceiveCommandResponseAsync(
/// Remove a message from a conversation
/// </summary>
/// <param name="conversationId">Conversation ID</param>
/// <param name="messageCreatedEvent">Message information</param>
/// <param name="message">Message information</param>
/// <param name="cancellationToken">Async task cancellation token</param>
public virtual async Task DeleteMessageAsync(
string conversationId,
Expand Down
2 changes: 2 additions & 0 deletions assistant-connector/dotnet/WorkbenchConnector/ConfigUtils.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Collections.Generic;

namespace Microsoft.SemanticWorkbench.Connector;

public static class ConfigUtils
Expand Down
2 changes: 2 additions & 0 deletions assistant-connector/dotnet/WorkbenchConnector/Constants.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Net.Http;

namespace Microsoft.SemanticWorkbench.Connector;

public static class Constants
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.

using System;

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

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using Microsoft.SemanticKernel.ChatCompletion;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Text.Json.Serialization;

// ReSharper disable once CheckNamespace
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Collections.Generic;

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

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Text.Json.Serialization;

// ReSharper disable once CheckNamespace
Expand Down Expand Up @@ -30,12 +31,19 @@ public class Message
[JsonPropertyName("metadata")]
public MessageMetadata Metadata { get; set; } = new();

/// <summary>
/// Prepare a chat message instance
/// <note>
/// Content types:
/// - text/plain
/// - text/html
/// - application/json (requires "json_schema" metadata)
/// </note>
/// </summary>
/// <param name="agentId">Agent ID</param>
/// <param name="content">Chat content</param>
/// <param name="debug">Optional debugging data</param>
/// <param name="contentType">Message content type</param>
public static Message CreateChatMessage(
string agentId,
string content,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

Expand Down Expand Up @@ -39,9 +44,9 @@ public AgentServiceStorage(
: "StoragePathLinux") ?? string.Empty;
this._path = Path.Join(tmpPath, connectorId);

if (this._path.Contains("$tmp"))
if (this._path.Contains("$tmp", StringComparison.OrdinalIgnoreCase))
{
this._path = this._path.Replace("$tmp", Path.GetTempPath());
this._path = this._path.Replace("$tmp", Path.GetTempPath(), StringComparison.OrdinalIgnoreCase);
}

this._path = Path.Join(this._path, "agents");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

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

Expand Down
32 changes: 18 additions & 14 deletions assistant-connector/dotnet/WorkbenchConnector/Webservice.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
Expand Down Expand Up @@ -295,22 +301,20 @@ public static IEndpointRouteBuilder UseFetchConversationInsightEndpoint(
return Results.NotFound($"State '{insightId}' Not Found");
}
else
// TODO: support schemas
var result = new
{
// TODO: support schemas
var result = new
id = insightId,
data = new
{
id = insightId,
data = new
{
content = insight.Content
},
json_schema = (object)null!,
ui_schema = (object)null!
};
content = insight.Content
},
json_schema = (object)null!,
ui_schema = (object)null!
};
return Results.Json(result);
}
return Results.Json(result);
});

return builder;
Expand Down Expand Up @@ -546,7 +550,7 @@ private static IEndpointRouteBuilder UseCatchAllEndpoint(
StringBuilder headersStringBuilder = new();
foreach (KeyValuePair<string, StringValues> header in context.Request.Headers)
{
headersStringBuilder.AppendLine($"{header.Key}: {header.Value}");
headersStringBuilder.AppendLine(CultureInfo.InvariantCulture, $"{header.Key}: {header.Value}");
}
// Read body
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.

using System;

namespace Microsoft.SemanticWorkbench.Connector;

public class WorkbenchConfig
Expand Down
Loading

0 comments on commit 5c3c3c0

Please sign in to comment.