From 5c3c3c0871555720cf4f1000f68e9f9b8e97a3ba Mon Sep 17 00:00:00 2001 From: Devis Lucato Date: Tue, 10 Sep 2024 21:18:44 -0700 Subject: [PATCH] Support for nuget package publishing (#39) Add nuget details Improve code docs Improve coding standard Add README for .NET connector --- assistant-connector/dotnet/README.md | 38 ++++++- .../dotnet/WorkbenchConnector/AgentBase.cs | 25 +++-- .../dotnet/WorkbenchConnector/ConfigUtils.cs | 2 + .../dotnet/WorkbenchConnector/Constants.cs | 2 + .../WorkbenchConnector/Models/Command.cs | 2 + .../WorkbenchConnector/Models/Conversation.cs | 2 + .../Models/ConversationEvent.cs | 1 + .../WorkbenchConnector/Models/DebugInfo.cs | 2 + .../WorkbenchConnector/Models/Message.cs | 8 ++ .../Storage/AgentServiceStorage.cs | 9 +- .../Storage/IAgentServiceStorage.cs | 4 + .../dotnet/WorkbenchConnector/Webservice.cs | 32 +++--- .../WorkbenchConnector/WorkbenchConfig.cs | 2 + .../WorkbenchConnector/WorkbenchConnector.cs | 85 ++++++++------- .../WorkbenchConnector.csproj | 99 +++++++++++++++++- .../dotnet/WorkbenchConnector/icon.png | Bin 0 -> 15525 bytes assistant-connector/dotnet/pack.sh | 8 ++ examples/dotnet-03-simple-chatbot/MyAgent.cs | 4 +- .../dotnet-03-simple-chatbot.csproj | 28 ++++- 19 files changed, 279 insertions(+), 74 deletions(-) create mode 100644 assistant-connector/dotnet/WorkbenchConnector/icon.png create mode 100755 assistant-connector/dotnet/pack.sh diff --git a/assistant-connector/dotnet/README.md b/assistant-connector/dotnet/README.md index ed8b2fb3..d4aaa1d1 100644 --- a/assistant-connector/dotnet/README.md +++ b/assistant-connector/dotnet/README.md @@ -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). diff --git a/assistant-connector/dotnet/WorkbenchConnector/AgentBase.cs b/assistant-connector/dotnet/WorkbenchConnector/AgentBase.cs index 8249a2d8..e0b90fe1 100644 --- a/assistant-connector/dotnet/WorkbenchConnector/AgentBase.cs +++ b/assistant-connector/dotnet/WorkbenchConnector/AgentBase.cs @@ -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; @@ -16,13 +20,13 @@ 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; } /// /// Agent instantiation @@ -30,7 +34,7 @@ public abstract class AgentBase /// Semantic Workbench connector /// Agent data storage /// Agent logger - public AgentBase( + protected AgentBase( WorkbenchConnector workbenchConnector, IAgentServiceStorage storage, ILogger log) @@ -68,6 +72,7 @@ public virtual AgentInfo ToDataModel() /// /// Start the agent /// + /// Async task cancellation token public virtual Task StartAsync( CancellationToken cancellationToken = default) { @@ -77,6 +82,7 @@ public virtual Task StartAsync( /// /// Stop the agent /// + /// Async task cancellation token public virtual Task StopAsync( CancellationToken cancellationToken = default) { @@ -158,7 +164,6 @@ await Task.WhenAll([ /// /// Delete a conversation /// - /// Agent instance ID /// Conversation ID /// Async task cancellation token public virtual Task DeleteConversationAsync( @@ -211,11 +216,11 @@ public virtual async Task AddParticipantAsync( /// Remove a participant from a conversation /// /// Conversation ID - /// Participant information + /// Participant information /// Async task cancellation token 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}'", @@ -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); } @@ -287,7 +292,7 @@ public virtual Task ReceiveNoteAsync( /// Receive a command, a special type of message /// /// Conversation ID - /// Message information + /// Command information /// Async task cancellation token public virtual Task ReceiveCommandAsync( string conversationId, @@ -321,7 +326,7 @@ public virtual Task ReceiveCommandResponseAsync( /// Remove a message from a conversation /// /// Conversation ID - /// Message information + /// Message information /// Async task cancellation token public virtual async Task DeleteMessageAsync( string conversationId, diff --git a/assistant-connector/dotnet/WorkbenchConnector/ConfigUtils.cs b/assistant-connector/dotnet/WorkbenchConnector/ConfigUtils.cs index 0ebcb9e6..7c4f7c42 100644 --- a/assistant-connector/dotnet/WorkbenchConnector/ConfigUtils.cs +++ b/assistant-connector/dotnet/WorkbenchConnector/ConfigUtils.cs @@ -1,5 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. +using System.Collections.Generic; + namespace Microsoft.SemanticWorkbench.Connector; public static class ConfigUtils diff --git a/assistant-connector/dotnet/WorkbenchConnector/Constants.cs b/assistant-connector/dotnet/WorkbenchConnector/Constants.cs index b771d868..fbb233b9 100644 --- a/assistant-connector/dotnet/WorkbenchConnector/Constants.cs +++ b/assistant-connector/dotnet/WorkbenchConnector/Constants.cs @@ -1,5 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. +using System.Net.Http; + namespace Microsoft.SemanticWorkbench.Connector; public static class Constants diff --git a/assistant-connector/dotnet/WorkbenchConnector/Models/Command.cs b/assistant-connector/dotnet/WorkbenchConnector/Models/Command.cs index 3abc36cb..35885410 100644 --- a/assistant-connector/dotnet/WorkbenchConnector/Models/Command.cs +++ b/assistant-connector/dotnet/WorkbenchConnector/Models/Command.cs @@ -1,5 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. +using System; + // ReSharper disable once CheckNamespace namespace Microsoft.SemanticWorkbench.Connector; diff --git a/assistant-connector/dotnet/WorkbenchConnector/Models/Conversation.cs b/assistant-connector/dotnet/WorkbenchConnector/Models/Conversation.cs index 34f24439..eb99e4e0 100644 --- a/assistant-connector/dotnet/WorkbenchConnector/Models/Conversation.cs +++ b/assistant-connector/dotnet/WorkbenchConnector/Models/Conversation.cs @@ -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; diff --git a/assistant-connector/dotnet/WorkbenchConnector/Models/ConversationEvent.cs b/assistant-connector/dotnet/WorkbenchConnector/Models/ConversationEvent.cs index 06de4707..daeaff65 100644 --- a/assistant-connector/dotnet/WorkbenchConnector/Models/ConversationEvent.cs +++ b/assistant-connector/dotnet/WorkbenchConnector/Models/ConversationEvent.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Text.Json.Serialization; // ReSharper disable once CheckNamespace diff --git a/assistant-connector/dotnet/WorkbenchConnector/Models/DebugInfo.cs b/assistant-connector/dotnet/WorkbenchConnector/Models/DebugInfo.cs index 2c549402..91446119 100644 --- a/assistant-connector/dotnet/WorkbenchConnector/Models/DebugInfo.cs +++ b/assistant-connector/dotnet/WorkbenchConnector/Models/DebugInfo.cs @@ -1,5 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. +using System.Collections.Generic; + // ReSharper disable once CheckNamespace namespace Microsoft.SemanticWorkbench.Connector; diff --git a/assistant-connector/dotnet/WorkbenchConnector/Models/Message.cs b/assistant-connector/dotnet/WorkbenchConnector/Models/Message.cs index a11ff1b6..7b4ffba1 100644 --- a/assistant-connector/dotnet/WorkbenchConnector/Models/Message.cs +++ b/assistant-connector/dotnet/WorkbenchConnector/Models/Message.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Text.Json.Serialization; // ReSharper disable once CheckNamespace @@ -30,12 +31,19 @@ public class Message [JsonPropertyName("metadata")] public MessageMetadata Metadata { get; set; } = new(); + /// + /// Prepare a chat message instance /// /// Content types: /// - text/plain /// - text/html /// - application/json (requires "json_schema" metadata) /// + /// + /// Agent ID + /// Chat content + /// Optional debugging data + /// Message content type public static Message CreateChatMessage( string agentId, string content, diff --git a/assistant-connector/dotnet/WorkbenchConnector/Storage/AgentServiceStorage.cs b/assistant-connector/dotnet/WorkbenchConnector/Storage/AgentServiceStorage.cs index ba996e18..0d0fb2a4 100644 --- a/assistant-connector/dotnet/WorkbenchConnector/Storage/AgentServiceStorage.cs +++ b/assistant-connector/dotnet/WorkbenchConnector/Storage/AgentServiceStorage.cs @@ -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; @@ -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"); diff --git a/assistant-connector/dotnet/WorkbenchConnector/Storage/IAgentServiceStorage.cs b/assistant-connector/dotnet/WorkbenchConnector/Storage/IAgentServiceStorage.cs index 6a81cbdf..e43b7d5f 100644 --- a/assistant-connector/dotnet/WorkbenchConnector/Storage/IAgentServiceStorage.cs +++ b/assistant-connector/dotnet/WorkbenchConnector/Storage/IAgentServiceStorage.cs @@ -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; diff --git a/assistant-connector/dotnet/WorkbenchConnector/Webservice.cs b/assistant-connector/dotnet/WorkbenchConnector/Webservice.cs index c990da59..4fc59aa3 100644 --- a/assistant-connector/dotnet/WorkbenchConnector/Webservice.cs +++ b/assistant-connector/dotnet/WorkbenchConnector/Webservice.cs @@ -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; @@ -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; @@ -546,7 +550,7 @@ private static IEndpointRouteBuilder UseCatchAllEndpoint( StringBuilder headersStringBuilder = new(); foreach (KeyValuePair header in context.Request.Headers) { - headersStringBuilder.AppendLine($"{header.Key}: {header.Value}"); + headersStringBuilder.AppendLine(CultureInfo.InvariantCulture, $"{header.Key}: {header.Value}"); } // Read body diff --git a/assistant-connector/dotnet/WorkbenchConnector/WorkbenchConfig.cs b/assistant-connector/dotnet/WorkbenchConnector/WorkbenchConfig.cs index a1cc706a..dbc14c6d 100644 --- a/assistant-connector/dotnet/WorkbenchConnector/WorkbenchConfig.cs +++ b/assistant-connector/dotnet/WorkbenchConnector/WorkbenchConfig.cs @@ -1,5 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. +using System; + namespace Microsoft.SemanticWorkbench.Connector; public class WorkbenchConfig diff --git a/assistant-connector/dotnet/WorkbenchConnector/WorkbenchConnector.cs b/assistant-connector/dotnet/WorkbenchConnector/WorkbenchConnector.cs index 75238d90..83ffa0f3 100644 --- a/assistant-connector/dotnet/WorkbenchConnector/WorkbenchConnector.cs +++ b/assistant-connector/dotnet/WorkbenchConnector/WorkbenchConnector.cs @@ -1,7 +1,13 @@ // Copyright (c) Microsoft. All rights reserved. +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; using System.Text; using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; @@ -9,15 +15,15 @@ namespace Microsoft.SemanticWorkbench.Connector; public abstract class WorkbenchConnector : IDisposable { - protected readonly IAgentServiceStorage Storage; - protected readonly WorkbenchConfig Config = new(); - protected readonly HttpClient HttpClient; - protected readonly ILogger Log; - protected readonly Dictionary Agents; + protected IAgentServiceStorage Storage { get; private set; } + protected WorkbenchConfig Config { get; private set; } = new(); + protected HttpClient HttpClient { get; private set; } + protected ILogger Log { get; private set; } + protected Dictionary Agents { get; private set; } private Timer? _pingTimer; - public WorkbenchConnector( + protected WorkbenchConnector( IConfiguration appConfig, IAgentServiceStorage storage, ILogger logger) @@ -40,9 +46,9 @@ public WorkbenchConnector( public virtual async Task ConnectAsync(CancellationToken cancellationToken = default) { this.Log.LogInformation("Connecting {1} {2} {3}...", this.Config.ConnectorName, this.Config.ConnectorId, this.Config.ConnectorEndpoint); - #pragma warning disable CS4014 // ping runs in the background without blocking +#pragma warning disable CS4014 // ping runs in the background without blocking this._pingTimer ??= new Timer(_ => this.PingSemanticWorkbenchBackendAsync(cancellationToken), null, 0, 10000); - #pragma warning restore CS4014 +#pragma warning restore CS4014 List agents = await this.Storage.GetAllAgentsAsync(cancellationToken).ConfigureAwait(false); this.Log.LogInformation("Starting {0} agents", agents.Count); @@ -56,12 +62,15 @@ public virtual async Task ConnectAsync(CancellationToken cancellationToken = def /// Disconnect the agent service from the workbench backend /// /// Async task cancellation token - public virtual Task DisconnectAsync(CancellationToken cancellationToken = default) + public virtual async Task DisconnectAsync(CancellationToken cancellationToken = default) { this.Log.LogInformation("Disconnecting {1} {2} ...", this.Config.ConnectorName, this.Config.ConnectorId); - this._pingTimer?.Dispose(); + if (this._pingTimer != null) + { + await this._pingTimer.DisposeAsync().ConfigureAwait(false); + } + this._pingTimer = null; - return Task.CompletedTask; } /// @@ -69,6 +78,7 @@ public virtual Task DisconnectAsync(CancellationToken cancellationToken = defaul /// /// Agent instance ID /// Agent name + /// Configuration content /// Async task cancellation token public abstract Task CreateAgentAsync( string agentId, @@ -110,7 +120,7 @@ public virtual async Task DeleteAgentAsync( /// /// Agent instance ID /// Conversation ID - /// Content. Markdown and HTML are supported. + /// Insight content. Markdown and HTML are supported. /// Async task cancellation token public virtual async Task UpdateAgentConversationInsightAsync( string agentId, @@ -137,8 +147,8 @@ public virtual async Task UpdateAgentConversationInsightAsync( }; string url = Constants.SendAgentConversationInsightsEvent.Path - .Replace(Constants.SendAgentConversationInsightsEvent.AgentPlaceholder, agentId) - .Replace(Constants.SendAgentConversationInsightsEvent.ConversationPlaceholder, conversationId); + .Replace(Constants.SendAgentConversationInsightsEvent.AgentPlaceholder, agentId, StringComparison.OrdinalIgnoreCase) + .Replace(Constants.SendAgentConversationInsightsEvent.ConversationPlaceholder, conversationId, StringComparison.OrdinalIgnoreCase); await this.SendAsync(HttpMethod.Post, url, data, agentId, "UpdateAgentConversationInsight", cancellationToken).ConfigureAwait(false); } @@ -167,18 +177,17 @@ public virtual async Task SetAgentStatusAsync( }; string url = Constants.SendAgentStatusMessage.Path - .Replace(Constants.SendAgentStatusMessage.ConversationPlaceholder, conversationId) - .Replace(Constants.SendAgentStatusMessage.AgentPlaceholder, agentId); + .Replace(Constants.SendAgentStatusMessage.ConversationPlaceholder, conversationId, StringComparison.OrdinalIgnoreCase) + .Replace(Constants.SendAgentStatusMessage.AgentPlaceholder, agentId, StringComparison.OrdinalIgnoreCase); await this.SendAsync(HttpMethod.Put, url, data, agentId, $"SetAgentStatus[{status}]", cancellationToken).ConfigureAwait(false); } /// - /// Set a temporary agent status within a conversation + /// Reset the temporary agent status within a conversation /// /// Agent instance ID /// Conversation ID - /// Short status description /// Async task cancellation token public virtual async Task ResetAgentStatusAsync( string agentId, @@ -188,18 +197,18 @@ public virtual async Task ResetAgentStatusAsync( this.Log.LogDebug("Setting agent status in conversation '{0}' with agent '{1}'", conversationId.HtmlEncode(), agentId.HtmlEncode()); - string payload = """ - { - "status": null, - "active_participant": true - } - """; + const string Payload = """ + { + "status": null, + "active_participant": true + } + """; - var data = JsonSerializer.Deserialize(payload); + var data = JsonSerializer.Deserialize(Payload); string url = Constants.SendAgentStatusMessage.Path - .Replace(Constants.SendAgentStatusMessage.ConversationPlaceholder, conversationId) - .Replace(Constants.SendAgentStatusMessage.AgentPlaceholder, agentId); + .Replace(Constants.SendAgentStatusMessage.ConversationPlaceholder, conversationId, StringComparison.OrdinalIgnoreCase) + .Replace(Constants.SendAgentStatusMessage.AgentPlaceholder, agentId, StringComparison.OrdinalIgnoreCase); await this.SendAsync(HttpMethod.Put, url, data!, agentId, "ResetAgentStatus", cancellationToken).ConfigureAwait(false); } @@ -221,7 +230,7 @@ public virtual async Task SendMessageAsync( conversationId.HtmlEncode(), agentId.HtmlEncode()); string url = Constants.SendAgentMessage.Path - .Replace(Constants.SendAgentMessage.ConversationPlaceholder, conversationId); + .Replace(Constants.SendAgentMessage.ConversationPlaceholder, conversationId, StringComparison.OrdinalIgnoreCase); await this.SendAsync(HttpMethod.Post, url, message, agentId, "SendMessage", cancellationToken).ConfigureAwait(false); } @@ -240,7 +249,7 @@ public virtual async Task GetFilesAsync( this.Log.LogDebug("Fetching list of files in conversation '{0}'", conversationId.HtmlEncode()); string url = Constants.GetConversationFiles.Path - .Replace(Constants.GetConversationFiles.ConversationPlaceholder, conversationId); + .Replace(Constants.GetConversationFiles.ConversationPlaceholder, conversationId, StringComparison.OrdinalIgnoreCase); HttpResponseMessage result = await this.SendAsync(HttpMethod.Get, url, null, agentId, "GetFiles", cancellationToken).ConfigureAwait(false); @@ -282,8 +291,8 @@ public virtual async Task DownloadFileAsync( this.Log.LogDebug("Downloading file from conversation '{0}'", conversationId.HtmlEncode()); string url = Constants.ConversationFile.Path - .Replace(Constants.ConversationFile.ConversationPlaceholder, conversationId) - .Replace(Constants.ConversationFile.FileNamePlaceholder, fileName); + .Replace(Constants.ConversationFile.ConversationPlaceholder, conversationId, StringComparison.OrdinalIgnoreCase) + .Replace(Constants.ConversationFile.FileNamePlaceholder, fileName, StringComparison.OrdinalIgnoreCase); HttpResponseMessage result = await this.SendAsync(HttpMethod.Get, url, null, agentId, "DownloadFile", cancellationToken).ConfigureAwait(false); @@ -316,7 +325,7 @@ public virtual async Task UploadFileAsync( this.Log.LogDebug("Deleting file {0} from a conversation '{1}'", fileName.HtmlEncode(), conversationId.HtmlEncode()); string url = Constants.UploadConversationFile.Path - .Replace(Constants.UploadConversationFile.ConversationPlaceholder, conversationId); + .Replace(Constants.UploadConversationFile.ConversationPlaceholder, conversationId, StringComparison.OrdinalIgnoreCase); // TODO: include file using multipart/form-data @@ -339,8 +348,8 @@ public virtual async Task DeleteFileAsync( this.Log.LogDebug("Deleting file {0} from a conversation '{1}'", fileName.HtmlEncode(), conversationId.HtmlEncode()); string url = Constants.ConversationFile.Path - .Replace(Constants.ConversationFile.ConversationPlaceholder, conversationId) - .Replace(Constants.ConversationFile.FileNamePlaceholder, fileName); + .Replace(Constants.ConversationFile.ConversationPlaceholder, conversationId, StringComparison.OrdinalIgnoreCase) + .Replace(Constants.ConversationFile.FileNamePlaceholder, fileName, StringComparison.OrdinalIgnoreCase); await this.SendAsync(HttpMethod.Delete, url, null, agentId, "DeleteFile", cancellationToken).ConfigureAwait(false); } @@ -349,7 +358,7 @@ public virtual async Task PingSemanticWorkbenchBackendAsync(CancellationToken ca { this.Log.LogTrace("Pinging workbench backend"); string path = Constants.AgentServiceRegistration.Path - .Replace(Constants.AgentServiceRegistration.Placeholder, this.Config.ConnectorId); + .Replace(Constants.AgentServiceRegistration.Placeholder, this.Config.ConnectorId, StringComparison.OrdinalIgnoreCase); var data = new { @@ -359,10 +368,10 @@ public virtual async Task PingSemanticWorkbenchBackendAsync(CancellationToken ca online_expires_in_seconds = 20 }; - await this.SendAsync(HttpMethod.Put, path, data, null, "PingSWBackend",cancellationToken).ConfigureAwait(false); + await this.SendAsync(HttpMethod.Put, path, data, null, "PingSWBackend", cancellationToken).ConfigureAwait(false); } -#region internals =========================================================================== + #region internals =========================================================================== public void Dispose() { @@ -432,5 +441,5 @@ protected virtual HttpRequestMessage PrepareRequest( return request; } -#endregion + #endregion } diff --git a/assistant-connector/dotnet/WorkbenchConnector/WorkbenchConnector.csproj b/assistant-connector/dotnet/WorkbenchConnector/WorkbenchConnector.csproj index 81089fdb..e640174c 100644 --- a/assistant-connector/dotnet/WorkbenchConnector/WorkbenchConnector.csproj +++ b/assistant-connector/dotnet/WorkbenchConnector/WorkbenchConnector.csproj @@ -1,11 +1,14 @@  - net8.0 - enable - enable Microsoft.SemanticWorkbench.Connector Microsoft.SemanticWorkbench.Connector + net8.0 + disable + enable + 12 + LatestMajor + $(NoWarn);IDE0130;CA2254;CA1812; @@ -16,4 +19,92 @@ - + + true + true + All + latest + + true + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + false + false + + + + true + true + + + + 0.1.0 + Microsoft.SemanticWorkbench.Connector + Connector for Agents and Assistants developed with Semantic Workbench + The connector allow Agents and Assistants to be used within Semantic Workbench. + Copilot, Agent, Agentic AI, Assistant, AI, Artificial Intelligence + bin/$(Configuration)/$(TargetFramework)/$(AssemblyName).xml + Microsoft + Microsoft + MIT + © Microsoft Corporation. All rights reserved. + https://github.com/microsoft/semanticworkbench + https://github.com/microsoft/semanticworkbench + true + icon.png + icon.png + README.md + true + true + full + true + snupkg + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assistant-connector/dotnet/WorkbenchConnector/icon.png b/assistant-connector/dotnet/WorkbenchConnector/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3862f148d4c56a92867623dda958bed1a15620ac GIT binary patch literal 15525 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 AgentExample AgentExample - $(NoWarn);SKEXP0010; + $(NoWarn);SKEXP0010;CA1861; @@ -17,11 +17,35 @@ + + + + true + true + All + latest + + true + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + all runtime; build; native; contentfiles; analyzers; buildtransitive - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + all runtime; build; native; contentfiles; analyzers; buildtransitive