From 7a608f5cb9da6f9a0a1b651e4b60cdc155720102 Mon Sep 17 00:00:00 2001 From: lilydu Date: Mon, 28 Oct 2024 16:24:47 -0700 Subject: [PATCH] add sensitivity label and citations --- .../Application/StreamingResponseTests.cs | 8 ++- .../Microsoft.TeamsAI/AI/Action/AIEntity.cs | 6 ++ .../Microsoft.TeamsAI/AI/Clients/LLMClient.cs | 5 +- .../Application/StreamingResponse.cs | 59 ++++++++++++++++++- 4 files changed, 74 insertions(+), 4 deletions(-) diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/StreamingResponseTests.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/StreamingResponseTests.cs index 5e65180ed..55e73f209 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/StreamingResponseTests.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI.Tests/Application/StreamingResponseTests.cs @@ -2,6 +2,8 @@ using AdaptiveCards; using Microsoft.Bot.Builder; using Microsoft.Bot.Schema; +using Microsoft.Teams.AI.AI.Action; +using Microsoft.Teams.AI.AI.Models; using Microsoft.Teams.AI.Application; using Microsoft.Teams.AI.Exceptions; using Microsoft.Teams.AI.Tests.TestUtils; @@ -216,12 +218,15 @@ void CaptureSend(Activity[] arg) from: new() { Id = "fromId" } )); StreamingResponse streamer = new(turnContext); - streamer.QueueTextChunk("first"); + List citations = new List(); + citations.Add(new Citation(content: "test-content", title: "test", url: "https://example.com")); + streamer.QueueTextChunk("first", citations); await streamer.WaitForQueue(); streamer.QueueTextChunk("second"); await streamer.WaitForQueue(); streamer.EnableFeedbackLoop = true; streamer.EnableGeneratedByAILabel = true; + streamer.SensitivityLabel = new SensitivityUsageInfo() { Name= "Sensitivity"}; await streamer.EndStream(); Assert.Equal(2, streamer.UpdatesSent()); } @@ -263,6 +268,7 @@ void CaptureSend(Activity[] arg) await streamer.EndStream(); Assert.Equal(2, streamer.UpdatesSent()); Assert.Single(streamer.Attachments); + Assert.Null(streamer.Citations); } [Fact] diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/AI/Action/AIEntity.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/AI/Action/AIEntity.cs index e0280b45b..79ebe7139 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/AI/Action/AIEntity.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/AI/Action/AIEntity.cs @@ -43,6 +43,12 @@ public class AIEntity : Entity /// [JsonProperty(PropertyName = "citation")] public List Citation { get; set; } = new(); + + /// + /// Optional sensitivity content information. + /// + [JsonProperty(PropertyName = "usageInfo")] + public SensitivityUsageInfo? UsageInfo { get; set; } } /// diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/AI/Clients/LLMClient.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/AI/Clients/LLMClient.cs index 3eb6c52d3..bee3b26ab 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/AI/Clients/LLMClient.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/AI/Clients/LLMClient.cs @@ -197,9 +197,12 @@ public async Task CompletePromptAsync( // Send chunk to client string text = args.Chunk.delta?.GetContent() ?? ""; + IList? citations = args.Chunk.delta?.Context?.Citations ?? null; + + if (text.Length > 0) { - streamer.QueueTextChunk(text); + streamer.QueueTextChunk(text, citations); } }); diff --git a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/StreamingResponse.cs b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/StreamingResponse.cs index fb844c5c5..4d8fc2d2c 100644 --- a/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/StreamingResponse.cs +++ b/dotnet/packages/Microsoft.TeamsAI/Microsoft.TeamsAI/Application/StreamingResponse.cs @@ -1,8 +1,11 @@ using Microsoft.Bot.Builder; using Microsoft.Bot.Schema; using Microsoft.Teams.AI.AI.Action; +using Microsoft.Teams.AI.AI.Models; using Microsoft.Teams.AI.Exceptions; +using Microsoft.Teams.AI.Utilities; using Newtonsoft.Json.Linq; +using System; namespace Microsoft.Teams.AI.Application { @@ -44,6 +47,16 @@ public class StreamingResponse /// public bool? EnableGeneratedByAILabel { get; set; } = false; + /// + /// The citations for the response. + /// + public List? Citations { get; set; } = new(); + + /// + /// The sensitivity label for the response. + /// + public SensitivityUsageInfo? SensitivityLabel { get; set; } + /// /// Gets the stream ID of the current response. /// Assigned after the initial update is sent. @@ -107,8 +120,9 @@ public void QueueInformativeUpdate(string text) /// Queues a chunk of partial message text to be sent to the client. /// /// Partial text of the message to send. + /// Citations to include in the message. /// Throws if the stream has already ended. - public void QueueTextChunk(string text) + public void QueueTextChunk(string text, IList? citations = null) { if (this._ended) { @@ -116,6 +130,40 @@ public void QueueTextChunk(string text) } Message += text; + + if (citations != null && citations.Count > 0) + { + if (this.Citations == null) + { + this.Citations = new List(); + } + + int currPos = this.Citations.Count; + + foreach (Citation citation in citations) + { + string abs = CitationUtils.Snippet(citation.Content, 480); + + this.Citations.Add(new ClientCitation() + { + Position = $"{currPos}", + Appearance = new ClientCitationAppearance() + { + Name = citation.Title, + Abstract = abs + } + }); + currPos++; + } + + // If there are citations, modify the content so that the sources are numbers instead of [doc1], [doc2], etc. + this.Message = this.Citations.Count == 0 ? this.Message : CitationUtils.FormatCitationsResponse(this.Message); + + // If there are citations, filter out the citations unused in content. + this.Citations = this.Citations.Count > 0 ? CitationUtils.GetUsedCitations(this.Message, this.Citations) : new List(); + + } + QueueNextChunk(); } @@ -283,7 +331,14 @@ private async Task SendActivity(Activity activity) // Add in Generated by AI if (this.EnableGeneratedByAILabel == true) { - activity.Entities.Add(new AIEntity()); + AIEntity entity = new AIEntity(); + if (this.Citations != null && this.Citations.Count > 0) + { + entity.Citation = this.Citations; + } + + entity.UsageInfo = this.SensitivityLabel; + activity.Entities.Add(entity); } }