diff --git a/src/AIAssistant/.aiassistignore b/src/AIAssistant/.aiassistignore index 6fa6d9f..2e59dd4 100644 --- a/src/AIAssistant/.aiassistignore +++ b/src/AIAssistant/.aiassistignore @@ -393,3 +393,45 @@ temp/ aiassist-config.json .aiassistignore .gitignore + +# Ignore common image file types +*.jpg +*.jpeg +*.png +*.gif +*.bmp +*.tiff +*.tif +*.webp +*.ico +*.svg + +# Ignore common video file types +*.mp4 +*.mov +*.avi +*.mkv +*.flv +*.wmv +*.webm +*.mpeg +*.mpg +*.m4v +*.3gp +*.3g2 + +# Ignore common document file types +*.doc +*.docx +*.xls +*.xlsx +*.ppt +*.pptx +*.pdf +*.txt +*.rtf +*.odt +*.ods +*.odp +*.csv +*.tsv \ No newline at end of file diff --git a/src/AIAssistant/AIAssistant.csproj b/src/AIAssistant/AIAssistant.csproj index b9a3bab..f8d7948 100644 --- a/src/AIAssistant/AIAssistant.csproj +++ b/src/AIAssistant/AIAssistant.csproj @@ -27,7 +27,6 @@ - @@ -39,7 +38,6 @@ - diff --git a/src/AIAssistant/Commands/CodeAssistCommand.cs b/src/AIAssistant/Commands/CodeAssistCommand.cs index 2a93061..c2d660d 100644 --- a/src/AIAssistant/Commands/CodeAssistCommand.cs +++ b/src/AIAssistant/Commands/CodeAssistCommand.cs @@ -45,10 +45,6 @@ public sealed class Settings : CommandSettings [Description("[grey] llm model for chatting with ai. for example llama3.1.[/].")] public string? ChatModel { get; set; } - [CommandOption("-t|--code-assist-type ")] - [Description("[grey] the type of code assist. it can be `embedding` or `summary`.[/].")] - public CodeAssistType? CodeAssistType { get; set; } - [CommandOption("-e|--embedding-model ")] [Description("[grey] llm model for embedding purpose. for example mxbai_embed_large.[/].")] public string? EmbeddingModel { get; set; } @@ -59,10 +55,24 @@ public sealed class Settings : CommandSettings [CommandOption("-d|--diff ")] [Description( - "[grey] the diff tool for showing changes. it can be `unifieddiff`, `codeblockdiff` and `mergeconflictdiff`.[/]." + "[grey] the diff tool for showing changes. it can be `unified-diff`, `code-block-diff` and `search-replace-diff`.[/]." )] public CodeDiffType? CodeDiffType { get; set; } + [CommandOption("-t|--code-assist-type ")] + [Description("[grey] the type of code assist. it can be `embedding` or `summary`.[/].")] + public CodeAssistType? CodeAssistType { get; set; } + + [CommandOption("--threshold ")] [Description("[grey] the chat model api key.[/].")] public string? ChatModelApiKey { get; set; } @@ -245,32 +255,33 @@ private void SetupOptions(Settings settings) if (settings.CodeDiffType is not null) { ArgumentException.ThrowIfNullOrEmpty(_llmOptions.ChatModel); - var model = cacheModels.GetModel(_llmOptions.ChatModel); - - switch (settings.CodeDiffType) - { - case CodeDiffType.UnifiedDiff: - model.ModelOption.CodeDiffType = CodeDiffType.UnifiedDiff; - break; - case CodeDiffType.CodeBlockDiff: - model.ModelOption.CodeDiffType = CodeDiffType.CodeBlockDiff; - break; - } + var chatModel = cacheModels.GetModel(_llmOptions.ChatModel); + chatModel.ModelOption.CodeDiffType = settings.CodeDiffType.Value; } if (settings.CodeAssistType is not null) { ArgumentException.ThrowIfNullOrEmpty(_llmOptions.ChatModel); - var model = cacheModels.GetModel(_llmOptions.ChatModel); - switch (settings.CodeAssistType) - { - case CodeAssistType.Embedding: - model.ModelOption.CodeAssistType = CodeAssistType.Embedding; - break; - case CodeAssistType.Summary: - model.ModelOption.CodeAssistType = CodeAssistType.Summary; - break; - } + var chatModel = cacheModels.GetModel(_llmOptions.ChatModel); + chatModel.ModelOption.CodeAssistType = settings.CodeAssistType.Value; + } + + if (settings.Threshold is not null) + { + ArgumentException.ThrowIfNullOrEmpty(_llmOptions.EmbeddingsModel); + var embeddingsModel = cacheModels.GetModel(_llmOptions.EmbeddingsModel); + embeddingsModel.ModelOption.Threshold = settings.Threshold.Value; + } + + if (settings.Temperature is not null) + { + ArgumentException.ThrowIfNullOrEmpty(_llmOptions.ChatModel); + var chatModel = cacheModels.GetModel(_llmOptions.ChatModel); + chatModel.ModelOption.Temperature = settings.Temperature.Value; + + ArgumentException.ThrowIfNullOrEmpty(_llmOptions.EmbeddingsModel); + var embeddingsModel = cacheModels.GetModel(_llmOptions.EmbeddingsModel); + embeddingsModel.ModelOption.Temperature = settings.Temperature.Value; } } } diff --git a/src/AIAssistant/Contracts/IContextService.cs b/src/AIAssistant/Contracts/IContextService.cs index e2b70f9..90cf2ed 100644 --- a/src/AIAssistant/Contracts/IContextService.cs +++ b/src/AIAssistant/Contracts/IContextService.cs @@ -7,6 +7,7 @@ public interface IContextService Context GetContext(); IList GetAllFiles(); IList GetFiles(IList? filesRelativePath); + void ValidateLoadedFilesLimit(); void AddContextFolder(string contextFolder); void AddOrUpdateFolder(IList? foldersRelativePath); void AddOrUpdateFiles(IList? filesRelativePath); diff --git a/src/AIAssistant/Contracts/ILLMClientManager.cs b/src/AIAssistant/Contracts/ILLMClientManager.cs index ff640f1..8e053f5 100644 --- a/src/AIAssistant/Contracts/ILLMClientManager.cs +++ b/src/AIAssistant/Contracts/ILLMClientManager.cs @@ -13,5 +13,9 @@ public interface ILLMClientManager string? systemPrompt, CancellationToken cancellationToken = default ); - Task GetEmbeddingAsync(string input, CancellationToken cancellationToken = default); + Task GetEmbeddingAsync( + string input, + string? path, + CancellationToken cancellationToken = default + ); } diff --git a/src/AIAssistant/Contracts/IPromptManager.cs b/src/AIAssistant/Contracts/IPromptManager.cs index 4b3da67..2f44a0d 100644 --- a/src/AIAssistant/Contracts/IPromptManager.cs +++ b/src/AIAssistant/Contracts/IPromptManager.cs @@ -15,7 +15,7 @@ public interface IPromptManager void AddPrompt(string embeddedResourceName, CommandType commandType, CodeDiffType? diffType); string GetPrompt(CommandType commandType, CodeDiffType? diffType, object? parameters); string AddCodeBlock(string treeSitterCode); - string AddEmbeddingInputString(string treeSitterCode); + string GetEmbeddingInputString(string treeSitterCode); string CreateLLMContext(IEnumerable codeBlocks); string FilesAddedToChat(IEnumerable fullFileContents); string? GetSystemPrompt(IList? codes, CodeAssistType codeAssistType, CodeDiffType diffType); diff --git a/src/AIAssistant/Diff/MergeConflictCodeDiffParser.cs b/src/AIAssistant/Diff/MergeConflictCodeDiffParser.cs deleted file mode 100644 index 35df9e2..0000000 --- a/src/AIAssistant/Diff/MergeConflictCodeDiffParser.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System.Text.RegularExpressions; -using AIAssistant.Contracts.Diff; -using AIAssistant.Models; - -namespace AIAssistant.Diff; - -// -// public class MergeConflictCodeDiffParser : ICodeDiffParser -// { -// // private static readonly Regex _filePathRegex = new(@"^([\w\-./\\]+?\.[\w]+)$", RegexOptions.Compiled); -// // private static readonly Regex _previousVersionStart = new(@"^<<<<<<< PREVIOUS VERSION$", RegexOptions.Compiled); -// // private static readonly Regex _newVersionEnd = new(@"^>>>>>>> NEW VERSION$", RegexOptions.Compiled); -// // private static readonly Regex _separator = new(@"^=======$", RegexOptions.Compiled); -// // -// // public IList GetFileChanges(string diff) -// // { -// // var changes = new List(); -// // string? currentFilePath = null; -// // var hunks = new List>(); -// // List? currentHunk = null; -// // -// // bool isPreviousVersion = false; -// // bool isNewVersion = false; -// // -// // var lines = diff.Split('\n'); -// // -// // foreach (var line in lines) -// // { -// // // Detect a new file path, starting a new `MergeConflict` section -// // if (_filePathRegex.IsMatch(line.Trim())) -// // { -// // // Finalize the previous file change if there are accumulated hunks -// // if (currentFilePath != null && hunks.Count > 0) -// // { -// // var fileChangeLines = hunks.SelectMany(h => h).ToList(); -// // var fileChangeType = DetermineFileChangeType(fileChangeLines); -// // changes.Add(new FileChange(currentFilePath, fileChangeType, fileChangeLines)); -// // hunks.Clear(); -// // } -// // -// // currentFilePath = line.Trim(); -// // continue; -// // } -// // -// // // Start of a new `PREVIOUS VERSION/NEW VERSION` hunk -// // if (_previousVersionStart.IsMatch(line.Trim())) -// // { -// // isPreviousVersion = true; -// // isNewVersion = false; -// // currentHunk = new List(); -// // continue; -// // } -// // -// // // Separator between previous and new version -// // if (_separator.IsMatch(line.Trim())) -// // { -// // isPreviousVersion = false; -// // isNewVersion = true; -// // continue; -// // } -// // -// // // End of the hunk's new version -// // if (_newVersionEnd.IsMatch(line.Trim())) -// // { -// // isNewVersion = false; -// // if (currentHunk != null) -// // { -// // hunks.Add(currentHunk); -// // currentHunk = null; -// // } -// // continue; -// // } -// // -// // // Collect lines within each `PREVIOUS VERSION` or `NEW VERSION` as part of the current hunk -// // if (isPreviousVersion && currentHunk != null) -// // { -// // currentHunk.Add(new FileChangeLine(0, line, CodeChangeType.Delete)); // 0 here because we're not tracking line numbers yet -// // } -// // else if (isNewVersion && currentHunk != null) -// // { -// // currentHunk.Add(new FileChangeLine(0, line, CodeChangeType.Add)); // 0 here because we're not tracking line numbers yet -// // } -// // } -// // -// // // Finalize the last file change if any hunks remain -// // if (currentFilePath != null && hunks.Count > 0) -// // { -// // var fileChangeLines = hunks.SelectMany(h => h).ToList(); -// // var fileChangeType = DetermineFileChangeType(fileChangeLines); -// // changes.Add(new FileChange(currentFilePath, fileChangeType, fileChangeLines)); -// // } -// // -// // return changes; -// // } -// // -// // private CodeChangeType DetermineFileChangeType(IList changeLines) -// // { -// // bool allAdded = changeLines.All(line => line.LineCodeChangeType == CodeChangeType.Add); -// // bool allDeleted = changeLines.All(line => line.LineCodeChangeType == CodeChangeType.Delete); -// // -// // if (allAdded) -// // return CodeChangeType.Add; // Newly created file -// // if (allDeleted) -// // return CodeChangeType.Delete; // Deleted file -// // -// // return CodeChangeType.Update; // Modified existing file -// // } -// } diff --git a/src/AIAssistant/Models/Options/AppOptions.cs b/src/AIAssistant/Models/Options/AppOptions.cs index de3869e..87d19e0 100644 --- a/src/AIAssistant/Models/Options/AppOptions.cs +++ b/src/AIAssistant/Models/Options/AppOptions.cs @@ -9,4 +9,6 @@ public class AppOptions public string ContextWorkingDirectory { get; set; } = default!; public bool AutoContextEnabled { get; set; } = true; public IList Files { get; set; } = new List(); + public int NumberOfFilesLimit { get; set; } = 500; + public int TreeLevel { get; set; } } diff --git a/src/AIAssistant/Prompts/PromptManager.cs b/src/AIAssistant/Prompts/PromptManager.cs index 0b9d8a1..49782b2 100644 --- a/src/AIAssistant/Prompts/PromptManager.cs +++ b/src/AIAssistant/Prompts/PromptManager.cs @@ -113,7 +113,7 @@ public string AddCodeBlock(string treeSitterCode) return renderBlock; } - public string AddEmbeddingInputString(string treeSitterCode) + public string GetEmbeddingInputString(string treeSitterCode) { return RenderPromptTemplate( AIAssistantConstants.Prompts.CodeEmbeddingTemplate, diff --git a/src/AIAssistant/Prompts/Templates/code-assist-merge-conflict-diff.template b/src/AIAssistant/Prompts/Templates/code-assist-merge-conflict-diff.template deleted file mode 100644 index 6952822..0000000 --- a/src/AIAssistant/Prompts/Templates/code-assist-merge-conflict-diff.template +++ /dev/null @@ -1,113 +0,0 @@ -You are an expert code assistant to analyze the provided code snippets and make necessary improvements, refactor or add new functionality based on the user's requests. -Your response should be in the markdown format. - -For understanding context you should consider following rules: -- Review the provided code context to understand the existing structure. Consider below `code context` for answering the user requests. -- Code context is containing multiple `code blocks` in `tree structures` format, actually for each `file path` there is a dedicated `code blocks` in tree structure format. -- Don't return response for `code blocks` in `tree structures` format. -- The code files are based on a `relative path` that exists in the `root` level of tree structure for each file and your response should be based on `relativePath`. -- You have following codes in your context: - -Create a Merge Conflict like format showing the specified changes to the provided code. Follow these guidelines to ensure the output is in a familiar, high-level, and flexible format: - -1. **MergeConflict Format:** -- For showing changes in a file use a `MergeConflict` format, and show file changes in multiple `PREVIOUS VERSION/NEW VERSION` blocks. -- In the **first line** of above `MergeConflict` format and before starting three backticks (```) contains a file `relative path` to the each file without any extra text just file relative path. -- In the **second line** of `MergeConflict` contains three backticks (```) as opening fence and code block used language to indicate the start of the file changes content. -- In the **third line** we have `<<<<<<< PREVIOUS VERSION` to indicate the start of the previous version of the code. -- In the **forth line** until reaching `=======` we have the old code before change. -- The line after old codes is `=======` which use to separate the previous and new versions -- The line after `=======`, we have the new code after change until reaching `>>>>>>> NEW VERSION`. -- The line after new code is `>>>>>>> NEW VERSION` which is end of new code. -- If MergeConflict format contains multiple `block of changes`, each `block of change` should start with a seperated `blank line` before starting next `>>>>>>> NEW VERSION` to mark the change block. -- The last line is three backticks (```) to close fences and end of the MergeConflict section. - -2. **Precise and Complete Changes:** - -- Include only the lines necessary to define the change, and some surrounding context if needed. -- Ensure the `PREVIOUS VERSION/NEW VERSION` blocks exactly matches the original file content. -- In the MergeConflict format, use multiple `block of change` with PREVIOUS VERSION/NEW VERSION pairs, separated by a blank line between blocks in a file, when changes are dispersed throughout different sections of a file. Each change block should focus on a single, specific modification. -- Each `PREVIOUS VERSION/NEW VERSION` block should include just enough lines around the change, ensuring uniqueness. -- For new files, leave the PREVIOUS VERSION section empty and show the full content under NEW VERSION. -- When code is removed entirely, show it only in the PREVIOUS VERSION section and leave the NEW VERSION section empty. -- If there are no requested changes (additions, deletions, or edits) in a file, do not create a block of change in MergeConflict section for it. -- When possible, include the entire function, method, conditional blocks, loops and etc code block that was modified, rather than only showing individual line edits. This approach makes the MergeConflict `clearer` and more `readable`. -- When editing an existing file, include only the specific lines to be updated or replaced. -- To move code within a file, include two separate blocks: one to delete the original code, and one to reinsert it in the new location. - -Here is a example for showing changes in unified diff format. bellow is the current class in `Project/Statistics.cs` file: - -**Original Code Block:** -``` csharp -using System; -using System.Collections.Generic; - -public class Statistics -{ - public double CalculateAverage(List numbers) - { - int sum = Sum(numbers); - return sum / (double)numbers.Count; - } - - private int Sum(List numbers) - { - int total = 0; - foreach (int number in numbers) - { - total += number; - } - return total; - } -} -``` - -Now after applying bellow changes on it, we get following MergeConflict format: - -1. Add a new using statement to import `System.Linq`. -2. Modify the `CalculateAverage()` method to use LINQ for computing the average instead of a manual loop. -3. Remove the `Sum()` method entirely, as it’s no longer needed with LINQ - -**MergeConflict format Output:** - -Project/Statistics.cs -```csharp -<<<<<<< PREVIOUS VERSION -using System; -using System.Collections.Generic; -======= -using System; -using System.Collections.Generic; -using System.Linq; ->>>>>>> NEW VERSION - -<<<<<<< PREVIOUS VERSION - public double CalculateAverage(List numbers) - { - int sum = Sum(numbers); - return sum / (double)numbers.Count; - } -======= - public double CalculateAverage(List numbers) - { - return numbers.Average(); - } ->>>>>>> NEW VERSION - -<<<<<<< PREVIOUS VERSION - private int Sum(List numbers) - { - int total = 0; - foreach (int number in numbers) - { - total += number; - } - return total; - } -======= ->>>>>>> NEW VERSION -``` - -For explanation of the produced response you should consider following rules: -- After providing the modified code sections in the `MergeConflict` format, include an `Explanation` section in the markdown result that summarizes each change. -- Clearly indicate what modifications were made in each file and the reasoning behind them. diff --git a/src/AIAssistant/Services/CodeAssistStrategies/EmbeddingCodeAssist.cs b/src/AIAssistant/Services/CodeAssistStrategies/EmbeddingCodeAssist.cs index 45d3cd9..5c5a153 100644 --- a/src/AIAssistant/Services/CodeAssistStrategies/EmbeddingCodeAssist.cs +++ b/src/AIAssistant/Services/CodeAssistStrategies/EmbeddingCodeAssist.cs @@ -31,7 +31,7 @@ public async Task LoadInitCodeFiles(string contextWorkingDirectory, IList x.CodeFileMap).ToList(); - await AddOrUpdateCodeFilesToCache(codeFileMaps, session); + await AddOrUpdateEmbeddingsForFiles(codeFileMaps, session); } public async Task AddOrUpdateCodeFiles(IList? codeFiles) @@ -44,7 +44,7 @@ public async Task AddOrUpdateCodeFiles(IList? codeFiles) var session = chatSessionManager.GetCurrentActiveSession(); var codeFileMaps = contextService.GetFiles(codeFiles).Select(x => x.CodeFileMap).ToList(); - await AddOrUpdateCodeFilesToCache(codeFileMaps, session); + await AddOrUpdateEmbeddingsForFiles(codeFileMaps, session); } public Task> GetCodeTreeContents(IList? codeFiles) @@ -104,15 +104,15 @@ public Task> GetCodeTreeContents(IList? codeFiles) } } - private async Task AddOrUpdateCodeFilesToCache(IList codeFileMaps, ChatSession chatSession) + private async Task AddOrUpdateEmbeddingsForFiles(IList codeFileMaps, ChatSession chatSession) { // generate embeddings data with using llms embeddings apis // https://ollama.com/blog/embedding-models // https://github.com/microsoft/semantic-kernel/blob/main/dotnet/notebooks/06-memory-and-embeddings.ipynb // https://github.com/chroma-core/chroma - var relatedEmbeddingsResult = await embeddingService.AddOrUpdateEmbeddingsForFiles(codeFileMaps, chatSession); + var filesEmbedding = await embeddingService.AddOrUpdateEmbeddingsForFiles(codeFileMaps, chatSession); - PrintEmbeddingCost(relatedEmbeddingsResult.TotalTokensCount, relatedEmbeddingsResult.TotalCost); + PrintEmbeddingCost(filesEmbedding.TotalTokensCount, filesEmbedding.TotalCost); } private void PrintEmbeddingCost(int totalCount, decimal totalCost) diff --git a/src/AIAssistant/Services/ContextService.cs b/src/AIAssistant/Services/ContextService.cs index 3556847..edb11ac 100644 --- a/src/AIAssistant/Services/ContextService.cs +++ b/src/AIAssistant/Services/ContextService.cs @@ -25,6 +25,8 @@ public void AddContextFolder(string contextFolder) { _currentContext.ContextItems.Add(folderItemContext); } + + ValidateLoadedFilesLimit(); } public void AddOrUpdateFolder(IList? foldersRelativePath) @@ -57,6 +59,8 @@ public void AddOrUpdateFolder(IList? foldersRelativePath) _currentContext.ContextItems.Add(folderItemsContext); } } + + ValidateLoadedFilesLimit(); } public void AddOrUpdateFiles(IList? filesRelativePath) @@ -86,6 +90,8 @@ public void AddOrUpdateFiles(IList? filesRelativePath) _currentContext.ContextItems.Add(fileItemContext); } } + + ValidateLoadedFilesLimit(); } public void AddOrUpdateUrls(IList? urls) @@ -152,6 +158,16 @@ public IList GetFiles(IList? filesRelativePath) return matchingFiles; } + public void ValidateLoadedFilesLimit() + { + if (GetAllFiles().Count == _appOptions.NumberOfFilesLimit) + { + throw new Exception( + $"File limit count {appOptions.Value.NumberOfFilesLimit} exceeded. You can ignore files and folders that are not necessary with adding them to '.aiassistignore' file or change the level of loading folders by setting 'AppOption.TreeLevel'" + ); + } + } + private void CollectFilesFromFolder(FolderItemContext folder, List fileList) { // Add all files from the folder @@ -192,11 +208,20 @@ bool useShortSummary try { var validFolders = folders.Where(folder => !fileService.IsPathIgnored(folder)).ToList(); + int treeLevel = _appOptions.TreeLevel; foreach (var folderPath in validFolders) { currentFolder = folderPath; - var subFoldersItemContext = InitSubFoldersItemContext(folderPath, contextWorkingDir, useShortSummary); + + // Start recursion with current depth set to 1 + var subFoldersItemContext = InitSubFoldersItemContext( + folderPath, + contextWorkingDir, + useShortSummary, + 1, + treeLevel + ); var filesItemsContext = InitFilesItemContext(folderPath, contextWorkingDir, useShortSummary); var folderRelativePath = Path.GetRelativePath(contextWorkingDir, folderPath).NormalizePath(); @@ -230,7 +255,9 @@ bool useShortSummary private IList InitSubFoldersItemContext( string folderPath, string contextWorkingDir, - bool useShortSummary + bool useShortSummary, + int currentDepth, + int treeLevel ) { IList subFolders = new List(); @@ -238,6 +265,13 @@ bool useShortSummary try { + // Stop recursion if the tree level is exceeded + if (treeLevel > 0 && currentDepth >= treeLevel) + { + // Return empty list as no deeper levels are loaded + return subFolders; + } + foreach ( var subFolder in Directory .GetDirectories(folderPath) @@ -249,11 +283,17 @@ var subFolder in Directory var subFolderFilesItemContext = InitFilesItemContext(subFolder, contextWorkingDir, useShortSummary); var subFolderRelativePath = Path.GetRelativePath(contextWorkingDir, subFolder).NormalizePath(); - // Recursive call for each subfolder + // Recursive call for each subfolder, incrementing the depth var subFolderContext = new FolderItemContext( subFolder, subFolderRelativePath, - InitSubFoldersItemContext(subFolder, contextWorkingDir, useShortSummary), + InitSubFoldersItemContext( + subFolder, + contextWorkingDir, + useShortSummary, + currentDepth + 1, + treeLevel + ), subFolderFilesItemContext ); diff --git a/src/AIAssistant/Services/EmbeddingService.cs b/src/AIAssistant/Services/EmbeddingService.cs index ca9407f..b8a1fdb 100644 --- a/src/AIAssistant/Services/EmbeddingService.cs +++ b/src/AIAssistant/Services/EmbeddingService.cs @@ -3,7 +3,8 @@ using AIAssistant.Data; using AIAssistant.Dtos; using AIAssistant.Models; -using AIAssistant.Prompts; +using BuildingBlocks.LLM; +using BuildingBlocks.Utils; using TreeSitter.Bindings.CustomTypes.TreeParser; namespace AIAssistant.Services; @@ -23,10 +24,11 @@ ChatSession chatSession decimal totalCost = 0; IList codeEmbeddings = new List(); + foreach (var codeFileMap in codeFilesMap) { - var input = promptManager.AddEmbeddingInputString(codeFileMap.TreeSitterFullCode); - var embeddingResult = await llmClientManager.GetEmbeddingAsync(input); + var input = promptManager.GetEmbeddingInputString(codeFileMap.TreeSitterFullCode); + var embeddingResult = await llmClientManager.GetEmbeddingAsync(input, codeFileMap.RelativePath); codeEmbeddings.Add( new CodeEmbedding @@ -80,6 +82,6 @@ public IEnumerable QueryByFilter( public async Task GenerateEmbeddingForUserInput(string userInput) { - return await llmClientManager.GetEmbeddingAsync(userInput); + return await llmClientManager.GetEmbeddingAsync(userInput, null); } } diff --git a/src/AIAssistant/Services/LLMClientManager.cs b/src/AIAssistant/Services/LLMClientManager.cs index 3c48098..1207300 100644 --- a/src/AIAssistant/Services/LLMClientManager.cs +++ b/src/AIAssistant/Services/LLMClientManager.cs @@ -88,11 +88,15 @@ ICacheModels cacheModels ); } - public async Task GetEmbeddingAsync(string input, CancellationToken cancellationToken = default) + public async Task GetEmbeddingAsync( + string input, + string? path, + CancellationToken cancellationToken = default + ) { var llmClientStratgey = _clientFactory.CreateClient(EmbeddingModel.ModelInformation.AIProvider); - var embeddingResponse = await llmClientStratgey.GetEmbeddingAsync(input, cancellationToken); + var embeddingResponse = await llmClientStratgey.GetEmbeddingAsync(input, path, cancellationToken); // in embedding output tokens and its cost is 0 var inputTokens = embeddingResponse?.TokenUsage?.InputTokens ?? await _tokenizer.GetTokenCount(input); diff --git a/src/Clients/AnthropicClient.cs b/src/Clients/AnthropicClient.cs index ac50945..a376046 100644 --- a/src/Clients/AnthropicClient.cs +++ b/src/Clients/AnthropicClient.cs @@ -37,7 +37,7 @@ AsyncPolicyWrap combinedPolicy CancellationToken cancellationToken = default ) { - await ValidateMaxInputToken(chatCompletionRequest); + await ValidateChatMaxInputToken(chatCompletionRequest); ValidateRequestSizeAndContent(chatCompletionRequest); var requestBody = new @@ -80,7 +80,7 @@ AsyncPolicyWrap combinedPolicy var inputCostPerToken = _chatModel.ModelInformation.InputCostPerToken; var outputCostPerToken = _chatModel.ModelInformation.OutputCostPerToken; - ValidateMaxToken(inputTokens + outTokens); + ValidateChatMaxToken(inputTokens + outTokens); return new ChatCompletionResponse( completionMessage, @@ -93,7 +93,7 @@ AsyncPolicyWrap combinedPolicy [EnumeratorCancellation] CancellationToken cancellationToken = default ) { - await ValidateMaxInputToken(chatCompletionRequest); + await ValidateChatMaxInputToken(chatCompletionRequest); ValidateRequestSizeAndContent(chatCompletionRequest); var requestBody = new @@ -165,7 +165,7 @@ AsyncPolicyWrap combinedPolicy var inputCostPerToken = _chatModel.ModelInformation.InputCostPerToken; var outputCostPerToken = _chatModel.ModelInformation.OutputCostPerToken; - ValidateMaxToken(inputTokens + outTokens); + ValidateChatMaxToken(inputTokens + outTokens); yield return new ChatCompletionResponse( null, @@ -193,7 +193,11 @@ AsyncPolicyWrap combinedPolicy } } - public Task GetEmbeddingAsync(string input, CancellationToken cancellationToken = default) + public Task GetEmbeddingAsync( + string input, + string? path, + CancellationToken cancellationToken = default + ) { throw new NotImplementedException(); } @@ -228,14 +232,11 @@ [NotNull] AnthropicChatResponse? anthropicChatResponse } } - private Task ValidateMaxInputToken(ChatCompletionRequest chatCompletionRequest) - { - return ValidateMaxInputToken(string.Concat(chatCompletionRequest.Items.Select(x => x.Prompt))); - } - - private async Task ValidateMaxInputToken(string input) + private async Task ValidateChatMaxInputToken(ChatCompletionRequest chatCompletionRequest) { - var inputTokenCount = await tokenizer.GetTokenCount(input); + var inputTokenCount = await tokenizer.GetTokenCount( + string.Concat(chatCompletionRequest.Items.Select(x => x.Prompt)) + ); if ( _chatModel.ModelInformation.MaxInputTokens > 0 @@ -247,14 +248,14 @@ private async Task ValidateMaxInputToken(string input) { StatusCode = (int)HttpStatusCode.BadRequest, Message = - $"'max_input_token' count: {inputTokenCount.FormatCommas()} is larger than configured 'max_input_token' count: {_chatModel.ModelInformation.MaxInputTokens.FormatCommas()}, if you need more tokens change the configuration.", + $"current chat 'max_input_token' count: {inputTokenCount.FormatCommas()} is larger than configured 'max_input_token' count: {_chatModel.ModelInformation.MaxInputTokens.FormatCommas()}", }, HttpStatusCode.BadRequest ); } } - private void ValidateMaxToken(int maxTokenCount) + private void ValidateChatMaxToken(int maxTokenCount) { if (_chatModel.ModelInformation.MaxTokens > 0 && maxTokenCount > _chatModel.ModelInformation.MaxTokens) { @@ -263,7 +264,7 @@ private void ValidateMaxToken(int maxTokenCount) { StatusCode = (int)HttpStatusCode.BadRequest, Message = - $"'max_token' count: {maxTokenCount.FormatCommas()} is larger than configured 'max_token' count: {_chatModel.ModelInformation.MaxTokens.FormatCommas()}, if you need more tokens change the configuration.", + $"current chat 'max_token' count: {maxTokenCount.FormatCommas()} is larger than configured 'max_token' count: {_chatModel.ModelInformation.MaxTokens.FormatCommas()}.", }, HttpStatusCode.BadRequest ); diff --git a/src/Clients/AzureClient.cs b/src/Clients/AzureClient.cs index ec81e3c..f030a63 100644 --- a/src/Clients/AzureClient.cs +++ b/src/Clients/AzureClient.cs @@ -39,7 +39,7 @@ AsyncPolicyWrap combinedPolicy CancellationToken cancellationToken = default ) { - await ValidateMaxInputToken(chatCompletionRequest); + await ValidateChatMaxInputToken(chatCompletionRequest); ValidateRequestSizeAndContent(chatCompletionRequest); // https://platform.openai.com/docs/api-reference/chat/create @@ -102,7 +102,7 @@ AsyncPolicyWrap combinedPolicy var inputCostPerToken = _chatModel.ModelInformation.InputCostPerToken; var outputCostPerToken = _chatModel.ModelInformation.OutputCostPerToken; - ValidateMaxToken(inputTokens + outTokens); + ValidateChatMaxToken(inputTokens + outTokens); return new ChatCompletionResponse( completionMessage, @@ -115,7 +115,7 @@ AsyncPolicyWrap combinedPolicy [EnumeratorCancellation] CancellationToken cancellationToken = default ) { - await ValidateMaxInputToken(chatCompletionRequest); + await ValidateChatMaxInputToken(chatCompletionRequest); ValidateRequestSizeAndContent(chatCompletionRequest); var requestBody = new @@ -219,7 +219,7 @@ AsyncPolicyWrap combinedPolicy var inputCostPerToken = _chatModel.ModelInformation.InputCostPerToken; var outputCostPerToken = _chatModel.ModelInformation.OutputCostPerToken; - ValidateMaxToken(inputTokens + outTokens); + ValidateChatMaxToken(inputTokens + outTokens); yield return new ChatCompletionResponse( null, @@ -240,10 +240,11 @@ AsyncPolicyWrap combinedPolicy public async Task GetEmbeddingAsync( string input, + string? path, CancellationToken cancellationToken = default ) { - await ValidateMaxInputToken(input); + await ValidateEmbeddingMaxInputToken(input); ValidateRequestSizeAndContent(input); var requestBody = new { input = new[] { input }, model = _embeddingModel.Name.Trim() }; @@ -294,7 +295,7 @@ AsyncPolicyWrap combinedPolicy var inputCostPerToken = _embeddingModel.ModelInformation.InputCostPerToken; var outputCostPerToken = _embeddingModel.ModelInformation.OutputCostPerToken; - ValidateMaxToken(inputTokens + outTokens); + ValidateEmbeddingMaxToken(inputTokens + outTokens, path); return new EmbeddingsResponse( embedding, @@ -329,33 +330,57 @@ private void HandleException(HttpResponseMessage httpResponse, [NotNull] OpenAIB } } - private Task ValidateMaxInputToken(ChatCompletionRequest chatCompletionRequest) + private async Task ValidateChatMaxInputToken(ChatCompletionRequest chatCompletionRequest) { - return ValidateMaxInputToken(string.Concat(chatCompletionRequest.Items.Select(x => x.Prompt))); + var inputTokenCount = await tokenizer.GetTokenCount( + string.Concat(chatCompletionRequest.Items.Select(x => x.Prompt), false) + ); + + if ( + _chatModel.ModelInformation.MaxInputTokens > 0 + && inputTokenCount > _chatModel.ModelInformation.MaxInputTokens + ) + { + throw new OpenAIException( + new OpenAIError + { + StatusCode = (int)HttpStatusCode.BadRequest, + Message = + $"current chat 'max_input_token' count: {inputTokenCount.FormatCommas()} is larger than configured 'max_input_token' count: {_chatModel.ModelInformation.MaxInputTokens.FormatCommas()}.", + }, + HttpStatusCode.BadRequest + ); + } } - private async Task ValidateMaxInputToken(string input) + private async Task ValidateEmbeddingMaxInputToken(string input, string? path = null) { var inputTokenCount = await tokenizer.GetTokenCount(input); if ( - _chatModel.ModelInformation.MaxInputTokens > 0 - && inputTokenCount > _chatModel.ModelInformation.MaxInputTokens + _embeddingModel.ModelInformation.MaxInputTokens > 0 + && inputTokenCount > _embeddingModel.ModelInformation.MaxInputTokens ) { + var moreInfo = path is not null + ? $"if file '{ + path + }' is not required for embedding you can ignore that by adding file or folder to '.aiassistignore'" + : ""; + throw new OpenAIException( new OpenAIError { StatusCode = (int)HttpStatusCode.BadRequest, Message = - $"'max_input_token' count: {inputTokenCount.FormatCommas()} is larger than configured 'max_input_token' count: {_chatModel.ModelInformation.MaxInputTokens.FormatCommas()}, if you need more tokens change the configuration.", + $"embedding {path} 'max_input_token' count: {inputTokenCount.FormatCommas()} is larger than configured 'max_input_token' count: {_embeddingModel.ModelInformation.MaxInputTokens.FormatCommas()}. {moreInfo}", }, HttpStatusCode.BadRequest ); } } - private void ValidateMaxToken(int maxTokenCount) + private void ValidateChatMaxToken(int maxTokenCount) { if (_chatModel.ModelInformation.MaxTokens > 0 && maxTokenCount > _chatModel.ModelInformation.MaxTokens) { @@ -364,7 +389,26 @@ private void ValidateMaxToken(int maxTokenCount) { StatusCode = (int)HttpStatusCode.BadRequest, Message = - $"'max_token' count: {maxTokenCount.FormatCommas()} is larger than configured 'max_token' count: {_chatModel.ModelInformation.MaxTokens.FormatCommas()}, if you need more tokens change the configuration.", + $"current chat 'max_token' count: {maxTokenCount.FormatCommas()} is larger than configured 'max_token' count: {_chatModel.ModelInformation.MaxTokens.FormatCommas()}.", + }, + HttpStatusCode.BadRequest + ); + } + } + + private void ValidateEmbeddingMaxToken(int maxTokenCount, string? path) + { + if ( + _embeddingModel.ModelInformation.MaxTokens > 0 + && maxTokenCount > _embeddingModel.ModelInformation.MaxTokens + ) + { + throw new OpenAIException( + new OpenAIError + { + StatusCode = (int)HttpStatusCode.BadRequest, + Message = + $"embedding {path} 'max_token' count: {maxTokenCount.FormatCommas()} is larger than configured 'max_token' count: {_embeddingModel.ModelInformation.MaxTokens.FormatCommas()}.", }, HttpStatusCode.BadRequest ); diff --git a/src/Clients/Contracts/ILLMClient.cs b/src/Clients/Contracts/ILLMClient.cs index 0b5b7e7..34dc934 100644 --- a/src/Clients/Contracts/ILLMClient.cs +++ b/src/Clients/Contracts/ILLMClient.cs @@ -13,5 +13,9 @@ public interface ILLMClient ChatCompletionRequest chatCompletionRequest, CancellationToken cancellationToken = default ); - Task GetEmbeddingAsync(string input, CancellationToken cancellationToken = default); + Task GetEmbeddingAsync( + string input, + string? path, + CancellationToken cancellationToken = default + ); } diff --git a/src/Clients/OllamaClient.cs b/src/Clients/OllamaClient.cs index 9c5c80c..a1d6553 100644 --- a/src/Clients/OllamaClient.cs +++ b/src/Clients/OllamaClient.cs @@ -39,7 +39,7 @@ AsyncPolicyWrap combinedPolicy CancellationToken cancellationToken = default ) { - await ValidateMaxInputToken(chatCompletionRequest); + await ValidateChatMaxInputToken(chatCompletionRequest); ValidateRequestSizeAndContent(chatCompletionRequest); // https://github.com/ollama/ollama/blob/main/docs/api.md#generate-a-chat-completion @@ -80,7 +80,7 @@ AsyncPolicyWrap combinedPolicy var inputCostPerToken = _chatModel.ModelInformation.InputCostPerToken; var outputCostPerToken = _chatModel.ModelInformation.OutputCostPerToken; - ValidateMaxToken(inputTokens + outTokens); + ValidateChatMaxToken(inputTokens + outTokens); return new ChatCompletionResponse( completionMessage, @@ -93,7 +93,7 @@ AsyncPolicyWrap combinedPolicy [EnumeratorCancellation] CancellationToken cancellationToken = default ) { - await ValidateMaxInputToken(chatCompletionRequest); + await ValidateChatMaxInputToken(chatCompletionRequest); ValidateRequestSizeAndContent(chatCompletionRequest); // https://github.com/ollama/ollama/blob/main/docs/api.md#generate-a-chat-completion @@ -159,7 +159,7 @@ AsyncPolicyWrap combinedPolicy var inputCostPerToken = _chatModel.ModelInformation.InputCostPerToken; var outputCostPerToken = _chatModel.ModelInformation.OutputCostPerToken; - ValidateMaxToken(inputTokens + outTokens); + ValidateChatMaxToken(inputTokens + outTokens); yield return new ChatCompletionResponse( completionMessage, @@ -183,10 +183,11 @@ AsyncPolicyWrap combinedPolicy public async Task GetEmbeddingAsync( string input, + string? path, CancellationToken cancellationToken = default ) { - await ValidateMaxInputToken(input); + await ValidateEmbeddingMaxInputToken(input, path); ValidateRequestSizeAndContent(input); // https://github.com/ollama/ollama/blob/main/docs/api.md#generate-embeddings @@ -225,7 +226,7 @@ AsyncPolicyWrap combinedPolicy var inputCostPerToken = _embeddingModel.ModelInformation.InputCostPerToken; var outputCostPerToken = _embeddingModel.ModelInformation.OutputCostPerToken; - ValidateMaxToken(inputTokens + outTokens); + ValidateEmbeddingMaxToken(inputTokens + outTokens, path); return new EmbeddingsResponse( embedding, @@ -251,33 +252,66 @@ private void HandleException(HttpResponseMessage httpResponse, [NotNull] OllamaR } } - private Task ValidateMaxInputToken(ChatCompletionRequest chatCompletionRequest) + private async Task ValidateChatMaxInputToken(ChatCompletionRequest chatCompletionRequest) { - return ValidateMaxInputToken(string.Concat(chatCompletionRequest.Items.Select(x => x.Prompt))); + var inputTokenCount = await tokenizer.GetTokenCount( + string.Concat(chatCompletionRequest.Items.Select(x => x.Prompt)) + ); + + if ( + _chatModel.ModelInformation.MaxInputTokens > 0 + && inputTokenCount > _chatModel.ModelInformation.MaxInputTokens + ) + { + throw new OllamaException( + $"current chat 'max_input_token' count: {inputTokenCount.FormatCommas()} is larger than configured 'max_input_token' count: {_chatModel.ModelInformation.MaxInputTokens.FormatCommas()}.", + HttpStatusCode.BadRequest + ); + } } - private async Task ValidateMaxInputToken(string input) + private async Task ValidateEmbeddingMaxInputToken(string input, string? path = null) { var inputTokenCount = await tokenizer.GetTokenCount(input); if ( - _chatModel.ModelInformation.MaxInputTokens > 0 - && inputTokenCount > _chatModel.ModelInformation.MaxInputTokens + _embeddingModel.ModelInformation.MaxInputTokens > 0 + && inputTokenCount > _embeddingModel.ModelInformation.MaxInputTokens ) { + var moreInfo = path is not null + ? $"if file '{ + path + }' is not required for embedding you can ignore that by adding file or folder to '.aiassistignore'" + : ""; + throw new OllamaException( - $"'max_input_token' count: {inputTokenCount.FormatCommas()} is larger than configured 'max_input_token' count: {_chatModel.ModelInformation.MaxInputTokens.FormatCommas()}, if you need more token change the configuration.", + $"embedding {path} 'max_input_token' count: {inputTokenCount.FormatCommas()} is larger than configured 'max_input_token' count: {_embeddingModel.ModelInformation.MaxInputTokens.FormatCommas()}. {moreInfo}", HttpStatusCode.BadRequest ); } } - private void ValidateMaxToken(int maxTokenCount) + private void ValidateChatMaxToken(int maxTokenCount) { if (_chatModel.ModelInformation.MaxTokens > 0 && maxTokenCount > _chatModel.ModelInformation.MaxTokens) { throw new OllamaException( - $"'max_token' count: {maxTokenCount.FormatCommas()} is larger than configured 'max_token' count: {_chatModel.ModelInformation.MaxTokens.FormatCommas()}, if you need more token change the configuration.", + $"current chat 'max_token' count: {maxTokenCount.FormatCommas()} is larger than configured 'max_token' count: {_chatModel.ModelInformation.MaxTokens.FormatCommas()}.", + HttpStatusCode.BadRequest + ); + } + } + + private void ValidateEmbeddingMaxToken(int maxTokenCount, string? path) + { + if ( + _embeddingModel.ModelInformation.MaxTokens > 0 + && maxTokenCount > _embeddingModel.ModelInformation.MaxTokens + ) + { + throw new OllamaException( + $"embedding {path} 'max_token' count: {maxTokenCount.FormatCommas()} is larger than configured 'max_token' count: {_embeddingModel.ModelInformation.MaxTokens.FormatCommas()}.", HttpStatusCode.BadRequest ); } diff --git a/src/Clients/OpenAiClient.cs b/src/Clients/OpenAiClient.cs index 27b78fd..424a3f4 100644 --- a/src/Clients/OpenAiClient.cs +++ b/src/Clients/OpenAiClient.cs @@ -38,7 +38,7 @@ AsyncPolicyWrap combinedPolicy CancellationToken cancellationToken = default ) { - await ValidateMaxInputToken(chatCompletionRequest); + await ValidateChatMaxInputToken(chatCompletionRequest); ValidateRequestSizeAndContent(chatCompletionRequest); // https://platform.openai.com/docs/api-reference/chat/create @@ -84,7 +84,7 @@ AsyncPolicyWrap combinedPolicy var inputCostPerToken = _chatModel.ModelInformation.InputCostPerToken; var outputCostPerToken = _chatModel.ModelInformation.OutputCostPerToken; - ValidateMaxToken(inputTokens + outTokens); + ValidateChatMaxToken(inputTokens + outTokens); return new ChatCompletionResponse( completionMessage, @@ -97,7 +97,7 @@ AsyncPolicyWrap combinedPolicy [EnumeratorCancellation] CancellationToken cancellationToken = default ) { - await ValidateMaxInputToken(chatCompletionRequest); + await ValidateChatMaxInputToken(chatCompletionRequest); ValidateRequestSizeAndContent(chatCompletionRequest); var requestBody = new @@ -184,7 +184,7 @@ AsyncPolicyWrap combinedPolicy var inputCostPerToken = _chatModel.ModelInformation.InputCostPerToken; var outputCostPerToken = _chatModel.ModelInformation.OutputCostPerToken; - ValidateMaxToken(inputTokens + outTokens); + ValidateChatMaxToken(inputTokens + outTokens); yield return new ChatCompletionResponse( null, @@ -205,10 +205,11 @@ AsyncPolicyWrap combinedPolicy public async Task GetEmbeddingAsync( string input, + string? path, CancellationToken cancellationToken = default ) { - await ValidateMaxInputToken(input); + await ValidateEmbeddingMaxInputToken(input, path); ValidateRequestSizeAndContent(input); var requestBody = new { input = new[] { input }, model = _embeddingModel.Name.Trim() }; @@ -242,7 +243,7 @@ AsyncPolicyWrap combinedPolicy var inputCostPerToken = _embeddingModel.ModelInformation.InputCostPerToken; var outputCostPerToken = _embeddingModel.ModelInformation.OutputCostPerToken; - ValidateMaxToken(inputTokens + outTokens); + ValidateEmbeddingMaxToken(inputTokens + outTokens, path); return new EmbeddingsResponse( embedding, @@ -277,33 +278,57 @@ private void HandleException(HttpResponseMessage httpResponse, [NotNull] OpenAIB } } - private Task ValidateMaxInputToken(ChatCompletionRequest chatCompletionRequest) + private async Task ValidateChatMaxInputToken(ChatCompletionRequest chatCompletionRequest) { - return ValidateMaxInputToken(string.Concat(chatCompletionRequest.Items.Select(x => x.Prompt))); + var inputTokenCount = await tokenizer.GetTokenCount( + string.Concat(chatCompletionRequest.Items.Select(x => x.Prompt)) + ); + + if ( + _chatModel.ModelInformation.MaxInputTokens > 0 + && inputTokenCount > _chatModel.ModelInformation.MaxInputTokens + ) + { + throw new OpenAIException( + new OpenAIError + { + StatusCode = (int)HttpStatusCode.BadRequest, + Message = + $"current chat 'max_input_token' count: {inputTokenCount.FormatCommas()} is larger than configured 'max_input_token' count: {_chatModel.ModelInformation.MaxInputTokens.FormatCommas()}", + }, + HttpStatusCode.BadRequest + ); + } } - private async Task ValidateMaxInputToken(string input) + private async Task ValidateEmbeddingMaxInputToken(string input, string? path = null) { var inputTokenCount = await tokenizer.GetTokenCount(input); if ( - _chatModel.ModelInformation.MaxInputTokens > 0 - && inputTokenCount > _chatModel.ModelInformation.MaxInputTokens + _embeddingModel.ModelInformation.MaxInputTokens > 0 + && inputTokenCount > _embeddingModel.ModelInformation.MaxInputTokens ) { + var moreInfo = path is not null + ? $"if file '{ + path + }' is not required for embedding you can ignore that by adding file or folder to '.aiassistignore'" + : ""; + throw new OpenAIException( new OpenAIError { StatusCode = (int)HttpStatusCode.BadRequest, Message = - $"'max_input_token' count: {inputTokenCount.FormatCommas()} is larger than configured 'max_input_token' count: {_chatModel.ModelInformation.MaxInputTokens.FormatCommas()}, if you need more tokens change the configuration.", + $"embedding {path} 'max_input_token' count: {inputTokenCount.FormatCommas()} is larger than configured 'max_input_token' count: {_embeddingModel.ModelInformation.MaxInputTokens.FormatCommas()}. {moreInfo}", }, HttpStatusCode.BadRequest ); } } - private void ValidateMaxToken(int maxTokenCount) + private void ValidateChatMaxToken(int maxTokenCount) { if (_chatModel.ModelInformation.MaxTokens > 0 && maxTokenCount > _chatModel.ModelInformation.MaxTokens) { @@ -312,7 +337,26 @@ private void ValidateMaxToken(int maxTokenCount) { StatusCode = (int)HttpStatusCode.BadRequest, Message = - $"'max_token' count: {maxTokenCount.FormatCommas()} is larger than configured 'max_token' count: {_chatModel.ModelInformation.MaxTokens.FormatCommas()}, if you need more tokens change the configuration.", + $"current chat 'max_token' count: {maxTokenCount.FormatCommas()} is larger than configured 'max_token' count: {_chatModel.ModelInformation.MaxTokens.FormatCommas()}.", + }, + HttpStatusCode.BadRequest + ); + } + } + + private void ValidateEmbeddingMaxToken(int maxTokenCount, string? path) + { + if ( + _embeddingModel.ModelInformation.MaxTokens > 0 + && maxTokenCount > _embeddingModel.ModelInformation.MaxTokens + ) + { + throw new OpenAIException( + new OpenAIError + { + StatusCode = (int)HttpStatusCode.BadRequest, + Message = + $"embedding {path} 'max_token' count: {maxTokenCount.FormatCommas()} is larger than configured 'max_token' count: {_chatModel.ModelInformation.MaxTokens.FormatCommas()}.", }, HttpStatusCode.BadRequest );