Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: ♻️ enhance markdown block rendering in spectre console #19

Merged
merged 3 commits into from
Nov 29, 2024
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
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
Loading