diff --git a/shell/agents/Microsoft.Azure.Agent/AzureAgent.cs b/shell/agents/Microsoft.Azure.Agent/AzureAgent.cs index e1049483..e88a4150 100644 --- a/shell/agents/Microsoft.Azure.Agent/AzureAgent.cs +++ b/shell/agents/Microsoft.Azure.Agent/AzureAgent.cs @@ -36,6 +36,7 @@ 7. DO NOT include the placeholder summary when the commands contains no placehol """; private int _turnsLeft; + private CopilotResponse _copilotResponse; private readonly string _instructions; private readonly StringBuilder _buffer; @@ -120,28 +121,28 @@ public async Task ChatAsync(string input, IShell shell) try { string query = $"{input}\n\n---\n\n{_instructions}"; - CopilotResponse copilotResponse = await host.RunWithSpinnerAsync( + _copilotResponse = await host.RunWithSpinnerAsync( status: "Thinking ...", spinnerKind: SpinnerKind.Processing, func: async context => await _chatSession.GetChatResponseAsync(query, context, token) ).ConfigureAwait(false); - if (copilotResponse is null) + if (_copilotResponse is null) { // User cancelled the operation. return true; } - if (copilotResponse.ChunkReader is null) + if (_copilotResponse.ChunkReader is null) { ArgPlaceholder?.DataRetriever?.Dispose(); ArgPlaceholder = null; // Process CLI handler response specially to support parameter injection. ResponseData data = null; - if (copilotResponse.TopicName == CopilotActivity.CLIHandlerTopic) + if (_copilotResponse.TopicName == CopilotActivity.CLIHandlerTopic) { - data = ParseCLIHandlerResponse(copilotResponse, shell); + data = ParseCLIHandlerResponse(shell); } if (data?.PlaceholderSet is not null) @@ -149,7 +150,7 @@ public async Task ChatAsync(string input, IShell shell) ArgPlaceholder = new ArgumentPlaceholder(input, data, _httpClient); } - string answer = data is null ? copilotResponse.Text : GenerateAnswer(data); + string answer = data is null ? _copilotResponse.Text : GenerateAnswer(data); host.RenderFullResponse(answer); } else @@ -161,12 +162,12 @@ public async Task ChatAsync(string input, IShell shell) while (true) { - CopilotActivity activity = copilotResponse.ChunkReader.ReadChunk(token); + CopilotActivity activity = _copilotResponse.ChunkReader.ReadChunk(token); if (activity is null) { prevActivity.ExtractMetadata(out string[] suggestion, out ConversationState state); - copilotResponse.SuggestedUserResponses = suggestion; - copilotResponse.ConversationState = state; + _copilotResponse.SuggestedUserResponses = suggestion; + _copilotResponse.ConversationState = state; break; } @@ -182,7 +183,7 @@ public async Task ChatAsync(string input, IShell shell) } } - var conversationState = copilotResponse.ConversationState; + var conversationState = _copilotResponse.ConversationState; _turnsLeft = conversationState.TurnLimit - conversationState.TurnNumber; if (_turnsLeft <= 5) { @@ -210,9 +211,9 @@ public async Task ChatAsync(string input, IShell shell) return true; } - private ResponseData ParseCLIHandlerResponse(CopilotResponse copilotResponse, IShell shell) + private ResponseData ParseCLIHandlerResponse(IShell shell) { - string text = copilotResponse.Text; + string text = _copilotResponse.Text; List codeBlocks = shell.ExtractCodeBlocks(text, out List sourceInfos); if (codeBlocks is null || codeBlocks.Count is 0) { @@ -264,7 +265,7 @@ private ResponseData ParseCLIHandlerResponse(CopilotResponse copilotResponse, IS Text = text, CommandSet = commands, PlaceholderSet = placeholders, - Locale = copilotResponse.Locale, + Locale = _copilotResponse.Locale, }; string first = placeholders[0].Name; diff --git a/shell/agents/Microsoft.Azure.Agent/ChatSession.cs b/shell/agents/Microsoft.Azure.Agent/ChatSession.cs index 117c497e..b9d54dd0 100644 --- a/shell/agents/Microsoft.Azure.Agent/ChatSession.cs +++ b/shell/agents/Microsoft.Azure.Agent/ChatSession.cs @@ -16,6 +16,7 @@ internal class ChatSession : IDisposable private string _token; private string _streamUrl; + private string _conversationId; private string _conversationUrl; private DateTime _expireOn; private AzureCopilotReceiver _copilotReceiver; @@ -24,6 +25,8 @@ internal class ChatSession : IDisposable private readonly HttpClient _httpClient; private readonly Dictionary _flights; + internal string ConversationId => _conversationId; + internal ChatSession(HttpClient httpClient) { _dl_secret = Environment.GetEnvironmentVariable("DL_SECRET"); @@ -88,6 +91,7 @@ private void Reset() { _token = null; _streamUrl = null; + _conversationId = null; _conversationUrl = null; _expireOn = DateTime.MinValue; @@ -135,7 +139,8 @@ private async Task StartConversationAsync(IHost host, CancellationToken cancella SessionPayload spl = JsonSerializer.Deserialize(content, Utils.JsonOptions); _token = spl.Token; - _conversationUrl = $"{CONVERSATION_URL}/{spl.ConversationId}/activities"; + _conversationId = spl.ConversationId; + _conversationUrl = $"{CONVERSATION_URL}/{_conversationId}/activities"; _streamUrl = spl.StreamUrl; _expireOn = DateTime.UtcNow.AddSeconds(spl.ExpiresIn); _copilotReceiver = await AzureCopilotReceiver.CreateAsync(_streamUrl); @@ -278,8 +283,8 @@ internal async Task GetChatResponseAsync(string input, IStatusC { CopilotResponse ret = activity.InputHint switch { - "typing" => new CopilotResponse(new ChunkReader(_copilotReceiver, activity), activity.Locale, activity.TopicName), - "acceptingInput" => new CopilotResponse(activity.Text, activity.Locale, activity.TopicName), + "typing" => new CopilotResponse(activity, new ChunkReader(_copilotReceiver, activity)), + "acceptingInput" => new CopilotResponse(activity), _ => throw CorruptDataException.Create($"The 'inputHint' is {activity.InputHint}.", activity) }; diff --git a/shell/agents/Microsoft.Azure.Agent/Schema.cs b/shell/agents/Microsoft.Azure.Agent/Schema.cs index 2b1a729f..e07ee904 100644 --- a/shell/agents/Microsoft.Azure.Agent/Schema.cs +++ b/shell/agents/Microsoft.Azure.Agent/Schema.cs @@ -102,30 +102,28 @@ internal class ConversationState internal class CopilotResponse { - internal CopilotResponse(ChunkReader chunkReader, string locale, string topicName) + internal CopilotResponse(CopilotActivity activity, ChunkReader chunkReader) + : this(activity) { ArgumentNullException.ThrowIfNull(chunkReader); - ArgumentException.ThrowIfNullOrEmpty(topicName); - ChunkReader = chunkReader; - Locale = locale; - TopicName = topicName; } - internal CopilotResponse(string text, string locale, string topicName) + internal CopilotResponse(CopilotActivity activity) { - ArgumentException.ThrowIfNullOrEmpty(text); - ArgumentException.ThrowIfNullOrEmpty(topicName); + ArgumentNullException.ThrowIfNull(activity); - Text = text; - Locale = locale; - TopicName = topicName; + Text = activity.Text; + Locale = activity.Locale; + TopicName = activity.TopicName; + ReplyToId = activity.ReplyToId; } internal ChunkReader ChunkReader { get; } internal string Text { get; } internal string Locale { get; } internal string TopicName { get; } + internal string ReplyToId { get; } internal string[] SuggestedUserResponses { get; set; } internal ConversationState ConversationState { get; set; } }