Skip to content

Commit

Permalink
refactor: ♻️ enhance markdown block rendering in spectre console (#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
mehdihadeli authored Nov 29, 2024
1 parent 594929e commit fec4cc3
Show file tree
Hide file tree
Showing 39 changed files with 646 additions and 674 deletions.
8 changes: 6 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,10 @@ jobs:
# https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-pack
- name: Pack NuGet Package Version ${{ steps.get_version.outputs.nuget_version }}
run: dotnet pack src/AIAssist/AIAssist.csproj -o ${{ env.NuGetDirectory }} -c Release --no-restore --no-build

# Publish the NuGet package as an artifact, so they can be used in the following jobs
- uses: actions/upload-artifact@v4
- name: Upload Package Version ${{ steps.get_version.outputs.nuget_version }}
uses: actions/upload-artifact@v4
with:
name: nuget
if-no-files-found: error
Expand All @@ -149,8 +151,10 @@ jobs:
with:
# https://github.com/dotnet/Nerdbank.GitVersioning/blob/main/doc/cloudbuild.md#github-actions
fetch-depth: 0 # doing deep clone and avoid shallow clone so nbgv can do its work.

# Download the NuGet package created in the previous job and copy in the root
- uses: actions/download-artifact@v4
- name: Download Nuget
uses: actions/download-artifact@v4
with:
name: nuget
## Optional. Default is $GITHUB_WORKSPACE
Expand Down
7 changes: 7 additions & 0 deletions AIAssistant.sln
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AIAssist", "AIAssist", "{4F
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AIAssist", "src\AIAssist\AIAssist.csproj", "{A4801AE4-5836-47CF-8AA4-DF99918BE2CC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuildingBlocks.UnitTests", "tests\UnitTests\BuildingBlocks.UnitTests\BuildingBlocks.UnitTests.csproj", "{496F9F39-89E5-4F9F-9DF4-8D5A86236C1D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -105,6 +107,7 @@ Global
{FA8BD80F-036B-4D0C-BE2D-62EA5FAF9C8C} = {6456834B-EA6C-48EA-9434-A4185D70F65F}
{4FAC7598-86FC-495F-B310-C641F424A904} = {97D741A1-DEFC-4241-99BC-7123A2D981E4}
{A4801AE4-5836-47CF-8AA4-DF99918BE2CC} = {4FAC7598-86FC-495F-B310-C641F424A904}
{496F9F39-89E5-4F9F-9DF4-8D5A86236C1D} = {CF49F264-D5C4-4A9F-BF9C-8706559CFC53}
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4973B5D3-67CE-47CC-A0C7-55EC268A2268}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
Expand All @@ -123,5 +126,9 @@ Global
{A4801AE4-5836-47CF-8AA4-DF99918BE2CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A4801AE4-5836-47CF-8AA4-DF99918BE2CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A4801AE4-5836-47CF-8AA4-DF99918BE2CC}.Release|Any CPU.Build.0 = Release|Any CPU
{496F9F39-89E5-4F9F-9DF4-8D5A86236C1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{496F9F39-89E5-4F9F-9DF4-8D5A86236C1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{496F9F39-89E5-4F9F-9DF4-8D5A86236C1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{496F9F39-89E5-4F9F-9DF4-8D5A86236C1D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
26 changes: 14 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
# AI Assist

> `Context Aware` AI assistant for coding, chat, code explanation, review with supporting local and online language models.
> `Context Aware` AI coding assistant inside terminal to help in code development, code explanation, code refactor and review, bug fix and chat with supporting local and online language models
`AIAssist` is compatible with [OpenAI](https://platform.openai.com/docs/api-reference/introduction) and [Azure AI Services](https://azure.microsoft.com/en-us/products/ai-services) through apis and [Ollama models](https://ollama.com/search) through [ollama engine](https://ollama.com/) locally.
`AIAssist` is compatible with bellow AI Services:
- [x] [OpenAI](https://platform.openai.com/docs/api-reference/introduction) through apis
- [x] [Azure AI Services](https://azure.microsoft.com/en-us/products/ai-services) through apis
- [x] [Ollama](https://ollama.com/) with using [ollama models](https://ollama.com/search) locally
- [ ] [Anthropic](https://docs.anthropic.com/en/api/getting-started) through apis
- [ ] [OpenRouter](https://openrouter.ai/docs/quick-start) through apis

> [!TIP]
> You can use ollama and its models that are more compatible with code like [deepseek-v2.5](https://ollama.com/library/deepseek-v2.5) or [qwen2.5-coder](https://ollama.com/library/qwen2.5-coder) locally. To use local models, you will need to run [Ollama](https://github.com/ollama/ollama) process first. For running ollama you can use [ollama docker](https://ollama.com/blog/ollama-is-now-available-as-an-official-docker-image) container.
Expand Down Expand Up @@ -30,16 +35,17 @@

AIAssist uses [Azure AI Services](https://azure.microsoft.com/en-us/products/ai-services) or [OpenAI](https://platform.openai.com/docs/api-reference/introduction) apis by default. For using `OpenAI` or `Azure AI` apis we need to have a `ApiKey`.

- Install `aiassist` with `dotnet tool install ` and bellow command:
- To access `dotnet tool`, we need to install [latest .net sdk](https://dotnet.microsoft.com/en-us/download) first.
- Install `aiassist` with `dotnet tool install` and bellow command:

```bash
TODO: Add Nuget Soon
dotnet tool install --global AIAssist
```

- For OpenAI If you don't have a API key you can [sign up](https://platform.openai.com/signup) in OpenAI and get a ApiKey.
- For Azure AI service you can [signup](https://azure.microsoft.com/en-us/products/ai-services) a azure account and get a AI model API key.
- After getting Api key we should set API key for chat and embedding models through environment variable or command options.
- Now got to `project directory` with `cd` command in terminal, For running `aiassist` and setting api key.
- For OpenAI If you don't have a API key you can [sign up](https://platform.openai.com/signup) in OpenAI and get a ApiKey.
- For Azure AI service you can [signup](https://azure.microsoft.com/en-us/products/ai-services) a azure account and get a AI model API key.
- After getting Api key we should set API key for chat and embedding models through environment variable or command options.
- Now got to `project directory` with `cd` command in terminal, For running `aiassist` and setting api key.

```bash
# Go to project directory
Expand All @@ -49,14 +55,12 @@ cd /to/project/directory
- Set `Api Key` through `environment variable`:

Linux terminal:

```bash
export CHAT_MODEL_API_KEY=your-chat-api-key-here
export EMBEDDINGS_MODEL_API_KEY=your-embedding-api-key-here
```

Windows Powershell Terminal:

```powershell
$env:CHAT_MODEL_API_KEY=your-chat-api-key-here
$env:EMBEDDINGS_MODEL_API_KEY=your-embedding-api-key-here
Expand All @@ -72,7 +76,6 @@ aiassist code --chat-api-key your-chat-api-key-here --embeddings-api-key your-e
- Set `ApiVersion`, `DeploymentId` and `BaseAddress` through`environment variable`:

Linux terminal:

```bash
export CHAT_BASE_ADDRESS=your-chat-base-address-here
export CHAT_API_VERSION=your-chat-api-version-here
Expand All @@ -83,7 +86,6 @@ export EMBEDDINGS_DEPLOYMENT_ID=your-embedding-deployment-id-here
```

Windows Powershell Terminal:

```powershell
$env:CHAT_BASE_ADDRESS=your-chat-base-address-here
$env:CHAT_API_VERSION=your-chat-api-version-here
Expand Down
3 changes: 0 additions & 3 deletions src/AIAssist/AIAssist.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@
<!-- https://tedspence.com/publishing-a-dotnet-tool-on-nuget-e1df7909ec5a-->
<!-- https://ml-software.ch/posts/versioning-made-easier-with-nerdbank-gitversioning-->
<!-- https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-pack#examples-->

<!-- dotnet pack -p:NuspecFile=AIAssist.nuspec -o nuget -c Debug /p:NuspecProperties="version=1.0.1"-->
<!-- dotnet tool install &#45;&#45;global AIAssist &#45;&#45;version 1.0.1 &#45;&#45;add-source .-->
<metadata>
<id>AIAssist</id>
<title>AIAssist</title>
Expand Down
29 changes: 10 additions & 19 deletions src/AIAssist/Commands/CodeAssistCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,17 +121,11 @@ public override async Task<int> ExecuteAsync(CommandContext context, Settings se

SetupOptions(settings);

spectreUtilities.InformationText("Code assist mode is activated!");
spectreUtilities.InformationText($"Chat model: {_chatModel.Name}");

if (_embeddingModel is not null)
{
spectreUtilities.InformationText($"Embedding model: {_embeddingModel.Name}");
}

spectreUtilities.InformationText($"CodeAssistType: {_chatModel.ModelOption.CodeAssistType}");
spectreUtilities.InformationText($"CodeDiffType: {_chatModel.ModelOption.CodeDiffType}");
spectreUtilities.InformationText("Please 'Ctrl+H' to see all available commands in the code assist mode.");
spectreUtilities.SummaryTextLine("Code assist mode is activated!");
spectreUtilities.SummaryTextLine(
$"Chat model: {_chatModel.Name} | Embedding model: {_embeddingModel?.Name ?? "-"} | CodeAssistType: {_chatModel.ModelOption.CodeAssistType} | CodeDiffType: {_chatModel.ModelOption.CodeDiffType}"
);
spectreUtilities.SummaryTextLine("Please 'Ctrl+H' to see all available commands in the code assist mode.");
spectreUtilities.WriteRule();

await AnsiConsole
Expand Down Expand Up @@ -166,7 +160,7 @@ await AnsiConsole

if (string.IsNullOrEmpty(userInput))
{
spectreUtilities.ErrorText("Input can't be null or empty string.");
spectreUtilities.ErrorTextLine("Input can't be null or empty string.");
continue;
}

Expand Down Expand Up @@ -251,25 +245,22 @@ private void SetupOptions(Settings settings)

if (settings.CodeDiffType is not null)
{
_chatModel.ModelOption.CodeDiffType = settings.CodeDiffType.Value;
_llmOptions.CodeDiffType = settings.CodeDiffType.Value;
}

if (settings.CodeAssistType is not null)
{
_chatModel.ModelOption.CodeAssistType = settings.CodeAssistType.Value;
_llmOptions.CodeAssistType = settings.CodeAssistType.Value;
}

if (settings.Threshold is not null && _embeddingModel is not null)
{
_embeddingModel.ModelOption.Threshold = settings.Threshold.Value;
_llmOptions.Threshold = settings.Threshold.Value;
}

if (settings.Temperature is not null)
{
_chatModel.ModelOption.Temperature = settings.Temperature.Value;

if (_embeddingModel is not null)
_embeddingModel.ModelOption.Temperature = settings.Temperature.Value;
_llmOptions.Temperature = settings.Temperature.Value;
}
}
}
Expand Down
23 changes: 23 additions & 0 deletions src/AIAssist/Commands/GenerateCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.ComponentModel;
using Spectre.Console.Cli;

namespace AIAssist.Commands;

[Description("Generate some settings and configs for the AI Assist.")]
public class GenerateCommand : AsyncCommand<GenerateCommand.Settings>
{
[CommandOption("-c|--config")]
[Description("[grey] generate base config file for the AIAssist.[/].")]
public bool GenerateConfig { get; set; }

[CommandOption("-i|--ignore")]
[Description("[grey] generate AIAssist ignore file.[/].")]
public bool GenerateIgnore { get; set; }

public sealed class Settings : CommandSettings { }

public override Task<int> ExecuteAsync(CommandContext context, Settings settings)
{
return null;
}
}
4 changes: 2 additions & 2 deletions src/AIAssist/Commands/InternalCommands/AddFileCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public Task<bool> ExecuteAsync(IServiceScope scope, string? input)
}
else
{
spectreUtilities.ErrorText($"The specified path does not exist: {path}");
spectreUtilities.ErrorTextLine($"The specified path does not exist: {path}");
}
}

Expand All @@ -56,7 +56,7 @@ public Task<bool> ExecuteAsync(IServiceScope scope, string? input)
}
}

spectreUtilities.InformationText(
spectreUtilities.InformationTextLine(
filesToAdd.Count != 0 ? $"Files added: {string.Join(", ", filesToAdd)}" : "No files were added."
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class ClearHistoryCommand(ISpectreUtilities spectreUtilities, IOptions<Ap

public Task<bool> ExecuteAsync(IServiceScope scope, string? input)
{
spectreUtilities.InformationText("History cleared.");
spectreUtilities.InformationTextLine("History cleared.");

return Task.FromResult(true);
}
Expand Down
2 changes: 1 addition & 1 deletion src/AIAssist/Commands/InternalCommands/QuitCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class QuitCommand(ISpectreUtilities spectreUtilities, IOptions<AppOptions

public Task<bool> ExecuteAsync(IServiceScope scope, string? input)
{
spectreUtilities.ErrorText("Process interrupted. Exiting...");
spectreUtilities.ErrorTextLine("Process interrupted. Exiting...");

// stop running commands
return Task.FromResult(false);
Expand Down
3 changes: 2 additions & 1 deletion src/AIAssist/Commands/InternalCommands/RunCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public async Task<bool> ExecuteAsync(IServiceScope scope, string? input)
var fullFilesContentForContext = await codeAssistantManager.GetCodeTreeContentsFromCache(requiredFiles);

var newQueryWithAddedFiles = promptManager.FilesAddedToChat(fullFilesContentForContext);
spectreUtilities.SuccessText(
spectreUtilities.SuccessTextLine(
$"{string.Join(",", requiredFiles.Select(file => $"'{file}'"))} added to the context."
);

Expand Down Expand Up @@ -84,5 +84,6 @@ private void PrintChatCost(ChatHistoryItem lastChatHistoryItem)
return;
spectreUtilities.WriteRule();
spectreUtilities.InformationText(message: lastChatHistoryItem.ChatCost.ToString(), justify: Justify.Right);
spectreUtilities.WriteRule();
}
}
30 changes: 15 additions & 15 deletions src/AIAssist/Diff/CodeDiffUpdater.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public void ApplyChanges(IList<DiffResult> diffResults, string contextWorkingDir

if (string.IsNullOrWhiteSpace(contextWorkingDirectory))
{
spectreUtilities.ErrorText("Working directory cannot be null or whitespace.");
spectreUtilities.ErrorTextLine("Working directory cannot be null or whitespace.");
}

foreach (var diffResult in diffResults)
Expand Down Expand Up @@ -53,11 +53,11 @@ private void HandleReplacementFile(DiffResult diffResult, string contextWorkingD
{
var updatedLines = ApplyReplacements(new List<string>(), diffResult.Replacements);
File.WriteAllText(modifiedFilePath, string.Join("\n", updatedLines));
spectreUtilities.SuccessText($"File created: {modifiedFilePath}");
spectreUtilities.SuccessTextLine($"File created: {modifiedFilePath}");
}
else
{
spectreUtilities.ErrorText("No modified lines provided for new file creation.");
spectreUtilities.ErrorTextLine("No modified lines provided for new file creation.");
}
}
else if (diffResult.ModifiedPath == noneExistPath && diffResult.OriginalPath != noneExistPath)
Expand All @@ -68,11 +68,11 @@ private void HandleReplacementFile(DiffResult diffResult, string contextWorkingD
if (File.Exists(originalFilePath) && diffResult.Replacements is not null && diffResult.Replacements.Any())
{
File.Delete(originalFilePath);
spectreUtilities.SuccessText($"File deleted: {originalFilePath}");
spectreUtilities.SuccessTextLine($"File deleted: {originalFilePath}");
}
else
{
spectreUtilities.ErrorText($"File not found for deletion: {originalFilePath}");
spectreUtilities.ErrorTextLine($"File not found for deletion: {originalFilePath}");
}
}
else if (diffResult.OriginalPath != diffResult.ModifiedPath)
Expand All @@ -94,7 +94,7 @@ private void HandleReplacementFile(DiffResult diffResult, string contextWorkingD
}
else
{
spectreUtilities.ErrorText($"Original file not found for rename/move: {originalFilePath}");
spectreUtilities.ErrorTextLine($"Original file not found for rename/move: {originalFilePath}");
}
}
else
Expand All @@ -108,7 +108,7 @@ private void HandleReplacementFile(DiffResult diffResult, string contextWorkingD

if (!File.Exists(originalFilePath))
{
spectreUtilities.ErrorText($"Original file not found: {originalFilePath}");
spectreUtilities.ErrorTextLine($"Original file not found: {originalFilePath}");
}

var originalLines = File.ReadAllLines(originalFilePath).ToList();
Expand All @@ -117,7 +117,7 @@ private void HandleReplacementFile(DiffResult diffResult, string contextWorkingD
Directory.CreateDirectory(Path.GetDirectoryName(modifiedFilePath)!);
File.WriteAllText(modifiedFilePath, string.Join("\n", updatedLines));

spectreUtilities.SuccessText($"File updated: {modifiedFilePath}");
spectreUtilities.SuccessTextLine($"File updated: {modifiedFilePath}");
}
}

Expand Down Expand Up @@ -167,7 +167,7 @@ string contextWorkingDirectory
Directory.CreateDirectory(Path.GetDirectoryName(modifiedFullPath)!);
// Normalize and write lines to prevent extra blank lines because WriteAllLines
File.WriteAllText(modifiedFullPath, string.Join("\n", modifiedLines));
spectreUtilities.SuccessText($"File created: {modifiedPath}");
spectreUtilities.SuccessTextLine($"File created: {modifiedPath}");

break;
}
Expand All @@ -180,11 +180,11 @@ string contextWorkingDirectory
{
// Normalize and write lines to prevent blank lines
File.WriteAllText(modifiedFullPath, string.Join("\n", modifiedLines));
spectreUtilities.SuccessText($"File updated: {modifiedPath}");
spectreUtilities.SuccessTextLine($"File updated: {modifiedPath}");
}
else
{
spectreUtilities.ErrorText($"File {modifiedPath} does not exist to modify.");
spectreUtilities.ErrorTextLine($"File {modifiedPath} does not exist to modify.");
}
break;
}
Expand All @@ -195,23 +195,23 @@ string contextWorkingDirectory
if (File.Exists(originalFullPath))
{
File.Delete(originalFullPath);
spectreUtilities.SuccessText($"File deleted: {originalPath}");
spectreUtilities.SuccessTextLine($"File deleted: {originalPath}");
}
else
{
spectreUtilities.ErrorText($"File {originalPath} not found for deletion.");
spectreUtilities.ErrorTextLine($"File {originalPath} not found for deletion.");
}
break;
}

default:
spectreUtilities.ErrorText($"Unsupported action type: {actionType}");
spectreUtilities.ErrorTextLine($"Unsupported action type: {actionType}");
break;
}
}
catch (Exception ex)
{
spectreUtilities.ErrorText($"Failed to update file {modifiedPath} \n {ex.Message}");
spectreUtilities.ErrorTextLine($"Failed to update file {modifiedPath} \n {ex.Message}");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -121,5 +121,6 @@ private void PrintEmbeddingCost(int totalCount, decimal totalCost)
message: $"Total Embedding Tokens: {totalCount.FormatCommas()} | Total Embedding Cost: ${totalCost.FormatCommas()}",
justify: Justify.Right
);
spectreUtilities.WriteRule();
}
}
Loading

0 comments on commit fec4cc3

Please sign in to comment.