Skip to content
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
34 changes: 24 additions & 10 deletions shell/agents/Microsoft.Azure.Agent/AzureAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public sealed class AzureAgent : ILLMAgent

internal ArgumentPlaceholder ArgPlaceholder { set; get; }

private const string SettingFileName = "az.agent.json";
private const string SettingFileName = "az.config.json";
private const string LoggingFileName = "log..txt";
private const string InstructionPrompt = """
NOTE: follow the below instructions when generating responses that include Azure CLI commands with placeholders:
Expand All @@ -37,6 +37,7 @@ 7. DO NOT include the placeholder summary when the commands contains no placehol

private int _turnsLeft;
private CopilotResponse _copilotResponse;
private AgentSetting _setting;

private readonly string _instructions;
private readonly StringBuilder _buffer;
Expand Down Expand Up @@ -84,17 +85,30 @@ public void Dispose()

public void Initialize(AgentConfig config)
{
_turnsLeft = int.MaxValue;
SettingFile = Path.Combine(config.ConfigurationRoot, SettingFileName);

string logFile = Path.Combine(config.ConfigurationRoot, LoggingFileName);
Log.Logger = new LoggerConfiguration()
.WriteTo.Async(a => a.File(
path: logFile,
outputTemplate: "{Timestamp:HH:mm:ss} [{Level:u3}] {Message:lj}{NewLine}{Exception}",
rollingInterval: RollingInterval.Day))
.CreateLogger();
Log.Information("Azure agent initialized.");
_turnsLeft = int.MaxValue;
_setting = AgentSetting.LoadFromFile(SettingFile);

if (_setting is null)
{
// Use default setting and create a setting file with the default settings.
_setting = AgentSetting.Default;
AgentSetting.NewSettingFile(SettingFile);
}

if (_setting.Logging)
{
string logFile = Path.Combine(config.ConfigurationRoot, LoggingFileName);
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Async(a => a.File(
path: logFile,
outputTemplate: "{Timestamp:HH:mm:ss} [{Level:u3}] {Message:lj}{NewLine}{Exception}",
rollingInterval: RollingInterval.Day))
.CreateLogger();
Log.Information("Azure agent initialized.");
}
}

public IEnumerable<CommandBase> GetCommands() => [new ReplaceCommand(this)];
Expand Down
6 changes: 4 additions & 2 deletions shell/agents/Microsoft.Azure.Agent/AzureCopilotReceiver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ internal static async Task<AzureCopilotReceiver> CreateAsync(string streamUrl)

private async Task ProcessActivities()
{
Log.Debug("[AzureCopilotReceiver] Receiver is up and running.");

while (_webSocket.State is WebSocketState.Open)
{
string closingMessage = null;
Expand All @@ -55,18 +57,18 @@ private async Task ProcessActivities()
{
closingMessage = "Close message received";
_activityQueue.Add(new CopilotActivity { Error = new ConnectionDroppedException("The server websocket is closing. Connection dropped.") });
Log.Information("[AzureCopilotReceiver] Web socket closed by server.");
}
}
catch (OperationCanceledException)
{
// Close the web socket before the thread is going away.
closingMessage = "Client closing";
Log.Error("[AzureCopilotReceiver] Receiver thread cancelled, which means the instance was disposed.");
Log.Information("[AzureCopilotReceiver] Receiver was cancelled and disposed.");
}

if (closingMessage is not null)
{
Log.Error("[AzureCopilotReceiver] Sending web socket closing request, message: '{0}'", closingMessage);
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, closingMessage, CancellationToken.None);
_activityQueue.CompleteAdding();
break;
Expand Down
3 changes: 3 additions & 0 deletions shell/agents/Microsoft.Azure.Agent/ChatSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Text.Json.Nodes;

using AIShell.Abstraction;
using Serilog;

namespace Microsoft.Azure.Agent;

Expand Down Expand Up @@ -145,6 +146,8 @@ private async Task StartConversationAsync(IHost host, CancellationToken cancella
_expireOn = DateTime.UtcNow.AddSeconds(spl.ExpiresIn);
_copilotReceiver = await AzureCopilotReceiver.CreateAsync(_streamUrl);

Log.Debug("[ChatSession] Conversation started. Id: {0}", _conversationId);

while (true)
{
CopilotActivity activity = _copilotReceiver.Take(cancellationToken);
Expand Down
3 changes: 3 additions & 0 deletions shell/agents/Microsoft.Azure.Agent/DataRetriever.cs
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,7 @@ private ArgumentInfo CreateArgInfo(ArgumentPair pair)
// Handle non-AzCLI command.
if (pair.Parameter is null)
{
Log.Debug("[DataRetriever] Non-AzCLI command: '{0}'", pair.Command);
return new ArgumentInfo(item.Name, item.Desc, dataType);
}

Expand Down Expand Up @@ -481,6 +482,8 @@ private List<string> GetArgValues(ArgumentPair pair)
string commandLine = $"{pair.Command} {pair.Parameter} ";
string tempFile = Path.GetTempFileName();

Log.Debug("[DataRetriever] Perform tab completion for '{0}'", commandLine);

try
{
using var process = new Process()
Expand Down
46 changes: 46 additions & 0 deletions shell/agents/Microsoft.Azure.Agent/Schema.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,56 @@
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using AIShell.Abstraction;

namespace Microsoft.Azure.Agent;

internal class AgentSetting
{
public bool Logging { get; set; }
public bool Telemetry { get; set; }

public AgentSetting()
{
// Enable logging and telemetry by default.
Logging = true;
Telemetry = true;
}

internal static AgentSetting Default => new();

internal static AgentSetting LoadFromFile(string path)
{
FileInfo file = new(path);
if (file.Exists)
{
try
{
using var stream = file.OpenRead();
return JsonSerializer.Deserialize<AgentSetting>(stream, Utils.JsonOptions);
}
catch (Exception e)
{
throw new InvalidDataException($"Parsing settings from '{path}' failed with the following error: {e.Message}", e);
}
}

return null;
}

internal static void NewSettingFile(string path)
{
const string content = """
{
"logging": true,
"telemetry": true
}
""";
File.WriteAllText(path, content, Encoding.UTF8);
}
}

internal class TokenPayload
{
public string ConversationId { get; set; }
Expand Down