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
5 changes: 4 additions & 1 deletion shell/agents/Microsoft.Azure.Agent/AzureAgent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,10 @@ internal void ReplaceKnownPlaceholders(ResponseData data)
{
string script = command.Script;
command.Script = script.Replace(entry.Key, entry.Value, StringComparison.OrdinalIgnoreCase);
command.Updated = !ReferenceEquals(script, command.Script);
if (!ReferenceEquals(script, command.Script))
{
command.Updated = true;
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion shell/agents/Microsoft.Azure.Agent/ChatSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ private async Task StartConversationAsync(IHost host, CancellationToken cancella
int chatNumber = conversationState.DailyConversationNumber;
int requestNumber = conversationState.TurnNumber;

host.WriteLine($"\n{activity.Text} This is chat #{chatNumber}, request #{requestNumber}.\n");
host.WriteLine($"\n{activity.Text}\nThis is chat #{chatNumber}, request #{requestNumber}.\n");
return;
}
}
Expand Down
22 changes: 14 additions & 8 deletions shell/agents/Microsoft.Azure.Agent/Command.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ private void ReplaceAction()
string subText = items.Count > 1
? $"all {items.Count} argument placeholders"
: "the argument placeholder";
host.WriteLine($"\nWe'll provide assistance in replacing {subText} and regenerating the result. You can press 'Enter' to skip to the next parameter or press 'Ctrl+c' to exit the assistance.\n");
host.WriteLine($"\nWe'll provide assistance in replacing {subText} and regenerating the result.\nYou can press 'Enter' to skip to the next parameter or press 'Ctrl+c' to exit the assistance.\n");
host.RenderDivider("Input Values", DividerAlignment.Left);
host.WriteLine();

Expand All @@ -72,13 +72,12 @@ private void ReplaceAction()
var item = items[i];
var (command, parameter) = dataRetriever.GetMappedCommand(item.Name);

string desc = item.Desc.TrimEnd('.');
string coloredCmd = parameter is null ? null : SyntaxHighlightAzCommand(command, parameter, item.Name);
string cmdPart = coloredCmd is null ? null : $" [{coloredCmd}]";

host.WriteLine(item.Type is "string"
? $"{i+1}. {desc}{cmdPart}"
: $"{i+1}. {desc}{cmdPart}. Value type: {item.Type}");
string caption = parameter is null ? item.Name : SyntaxHighlightAzCommand(command, parameter, item.Name);
host.WriteLine($"{i+1}. {caption}");
if (item.Name != item.Desc)
{
host.WriteLine(item.Desc);
}

// Get the task for creating the 'ArgumentInfo' object and show a spinner
// if we have to wait for the task to complete.
Expand Down Expand Up @@ -109,6 +108,13 @@ private void ReplaceAction()
string value = host.PromptForArgument(argInfo, printCaption: false);
if (!string.IsNullOrEmpty(value))
{
// Add quotes for the value if needed.
value = value.Trim();
if (value.StartsWith('-') || value.Contains(' '))
{
value = $"\"{value}\"";
}

_values.Add(item.Name, value);
_agent.SaveUserValue(item.Name, value);

Expand Down
80 changes: 42 additions & 38 deletions shell/agents/Microsoft.Azure.Agent/DataRetriever.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,15 +236,15 @@ static DataRetriever()
new("Container Registry",
"cr",
"The name only allows alphanumeric characters. Length: 5 to 50 chars.",
"cr<product>[<environment>][<identifier>]",
"cr<prod>[<env>][<id>]",
["crnavigatorprod001", "crhadoopdev001"],
"az acr create --name",
"New-AzContainerRegistry -Name"),

new("Storage Account",
"st",
"The name can only contain lowercase letters and numbers. Length: 3 to 24 chars.",
"st<product>[<environment>][<identifier>]",
"st<prod>[<env>][<id>]",
["stsalesappdataqa", "sthadoopoutputtest"],
"az storage account create --name",
"New-AzStorageAccount -Name"),
Expand Down Expand Up @@ -324,38 +324,50 @@ private void PairPlaceholders(ResponseData data)

foreach (var cmd in data.CommandSet)
{
string script = cmd.Script.Trim();
bool placeholderFound = false;
// Az Copilot may return a code block that contains multiple commands.
string[] scripts = cmd.Script.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);

// Handle AzCLI commands.
if (script.StartsWith("az ", StringComparison.OrdinalIgnoreCase))
foreach (string script in scripts)
{
if (!cmds.TryGetValue(script, out command))
// Handle AzCLI commands.
if (script.StartsWith("az ", StringComparison.OrdinalIgnoreCase))
{
int firstParamIndex = script.IndexOf("--");
command = script.AsSpan(0, firstParamIndex).Trim().ToString();
cmds.Add(script, command);
}
if (!cmds.TryGetValue(script, out command))
{
int firstParamIndex = script.IndexOf("--");
command = script.AsSpan(0, firstParamIndex).Trim().ToString();
cmds.Add(script, command);
}

int argIndex = script.IndexOf(item.Name, StringComparison.OrdinalIgnoreCase);
if (argIndex is -1)
{
continue;
int argIndex = script.IndexOf(item.Name, StringComparison.OrdinalIgnoreCase);
if (argIndex is -1)
{
continue;
}

int paramIndex = script.LastIndexOf("--", argIndex);
parameter = script.AsSpan(paramIndex, argIndex - paramIndex).Trim().ToString();

placeholderFound = true;
break;
}

int paramIndex = script.LastIndexOf("--", argIndex);
parameter = script.AsSpan(paramIndex, argIndex - paramIndex).Trim().ToString();
// It's a non-AzCLI command, such as "ssh".
if (script.Contains(item.Name, StringComparison.OrdinalIgnoreCase))
{
// Leave the parameter to be null for non-AzCLI commands, as there is
// no reliable way to parse an arbitrary command
command = script;
parameter = null;

break;
placeholderFound = true;
break;
}
}

// It's a non-AzCLI command, such as "ssh".
if (script.Contains(item.Name, StringComparison.OrdinalIgnoreCase))
if (placeholderFound)
{
// Leave the parameter to be null for non-AzCLI commands, as there is
// no reliable way to parse an arbitrary command
command = script;
parameter = null;

break;
}
}
Expand Down Expand Up @@ -421,12 +433,7 @@ private ArgumentInfo CreateArgInfo(ArgumentPair pair)
string cmdAndParam = $"{pair.Command} {pair.Parameter}";
if (s_azNamingRules.TryGetValue(cmdAndParam, out NamingRule rule))
{
string restriction = rule.PatternText is null
? rule.GeneralRule
: $"""
- {rule.GeneralRule}
- Recommended pattern: {rule.PatternText}, e.g. {string.Join(", ", rule.Example)}.
""";
string restriction = rule.PatternText is null ? null : $"Recommended pattern: {rule.PatternText}";
return new ArgumentInfoWithNamingRule(item.Name, item.Desc, restriction, rule);
}

Expand All @@ -439,14 +446,11 @@ private ArgumentInfo CreateArgInfo(ArgumentPair pair)

if (_stop) { return null; }

List<string> suggestions = GetArgValues(pair, out Option option);
// If the option's description is less than the placeholder's description in length, then it's
// unlikely to provide more information than the latter. In that case, we don't use it.
string optionDesc = option?.Description?.Length > item.Desc.Length ? option.Description : null;
return new ArgumentInfo(item.Name, item.Desc, optionDesc, dataType, suggestions);
List<string> suggestions = GetArgValues(pair);
return new ArgumentInfo(item.Name, item.Desc, restriction: null, dataType, suggestions);
}

private List<string> GetArgValues(ArgumentPair pair, out Option option)
private List<string> GetArgValues(ArgumentPair pair)
{
// First, try to get static argument values if they exist.
string command = pair.Command;
Expand All @@ -466,7 +470,7 @@ private List<string> GetArgValues(ArgumentPair pair, out Option option)
s_azStaticDataCache.TryAdd(command, commandData);
}

option = commandData?.FindOption(pair.Parameter);
Option option = commandData?.FindOption(pair.Parameter);
List<string> staticValues = option?.Arguments;
if (staticValues?.Count > 0)
{
Expand Down Expand Up @@ -646,7 +650,7 @@ internal NamingRule(

if (abbreviation is not null)
{
PatternText = $"<product>-{abbreviation}[-<environment>][-<identifier>]";
PatternText = $"<prod>-{abbreviation}[-<env>][-<id>]";
PatternRegex = new Regex($"^(?<prod>[a-zA-Z0-9]+)-{abbreviation}(?:-(?<env>[a-zA-Z0-9]+))?(?:-[a-zA-Z0-9]+)?$", RegexOptions.Compiled);

string product = s_products[Random.Shared.Next(0, s_products.Length)];
Expand Down