mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2025-04-28 15:39:46 +00:00
Implemented writing generated code to disk
This commit is contained in:
parent
170cda428c
commit
cbb0a36198
@ -337,5 +337,13 @@ else
|
||||
AI has made in which files.
|
||||
</MudText>
|
||||
|
||||
<MudText Typo="Typo.body1" Class="mb-2">
|
||||
When you rebuild / re-generate the ERI server code, AI Studio proceeds as follows: All files generated last time will be deleted. All
|
||||
other files you have created remain. Then, the AI generates the new files. <b>But beware:</b> It may happen that the AI generates a
|
||||
file this time that you manually created last time. In this case, your manually created file will then be overwritten. Therefore,
|
||||
you should always create a Git repository and commit or revert all changes before using this assistant. With a diff visualization,
|
||||
you can immediately see where the AI has made changes. It is best to use an IDE suitable for your selected language for this purpose.
|
||||
</MudText>
|
||||
|
||||
<MudTextSwitch Label="Should we write the generated code to the file system?" Disabled="@this.IsNoneERIServerSelected" @bind-Value="@this.writeToFilesystem" LabelOn="Yes, please write or update all generated code to the file system" LabelOff="No, just show me the code" />
|
||||
<SelectDirectory Label="Base directory where to write the code" @bind-Directory="@this.baseDirectory" Disabled="@(this.IsNoneERIServerSelected || !this.writeToFilesystem)" DirectoryDialogTitle="Select the target directory for the ERI server"/>
|
||||
|
@ -335,6 +335,7 @@ public partial class AssistantERI : AssistantBaseCore
|
||||
this.additionalLibraries = string.Empty;
|
||||
this.writeToFilesystem = false;
|
||||
this.baseDirectory = string.Empty;
|
||||
this.previouslyGeneratedFiles = new();
|
||||
}
|
||||
}
|
||||
|
||||
@ -366,6 +367,7 @@ public partial class AssistantERI : AssistantBaseCore
|
||||
this.additionalLibraries = this.selectedERIServer.AdditionalLibraries;
|
||||
this.writeToFilesystem = this.selectedERIServer.WriteToFilesystem;
|
||||
this.baseDirectory = this.selectedERIServer.BaseDirectory;
|
||||
this.previouslyGeneratedFiles = this.selectedERIServer.PreviouslyGeneratedFiles;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -422,6 +424,7 @@ public partial class AssistantERI : AssistantBaseCore
|
||||
this.selectedERIServer.AdditionalLibraries = this.additionalLibraries;
|
||||
this.selectedERIServer.WriteToFilesystem = this.writeToFilesystem;
|
||||
this.selectedERIServer.BaseDirectory = this.baseDirectory;
|
||||
this.selectedERIServer.PreviouslyGeneratedFiles = this.previouslyGeneratedFiles;
|
||||
await this.SettingsManager.StoreSettings();
|
||||
}
|
||||
|
||||
@ -448,6 +451,7 @@ public partial class AssistantERI : AssistantBaseCore
|
||||
private string additionalLibraries = string.Empty;
|
||||
private bool writeToFilesystem;
|
||||
private string baseDirectory = string.Empty;
|
||||
private List<string> previouslyGeneratedFiles = new();
|
||||
|
||||
private bool AreServerPresetsBlocked => !this.SettingsManager.ConfigurationData.ERI.PreselectOptions;
|
||||
|
||||
@ -905,6 +909,17 @@ public partial class AssistantERI : AssistantBaseCore
|
||||
yield return file;
|
||||
}
|
||||
|
||||
[GeneratedRegex("""
|
||||
\s*#+\s+[\w\\/.]+\s*```\w*\s+([\s\w\W]+)\s*```\s*
|
||||
""", RegexOptions.Singleline)]
|
||||
private static partial Regex CodeExtractRegex();
|
||||
|
||||
private string ExtractCode(string markdown)
|
||||
{
|
||||
var match = CodeExtractRegex().Match(markdown);
|
||||
return match.Success ? match.Groups[1].Value : string.Empty;
|
||||
}
|
||||
|
||||
private async Task GenerateServer()
|
||||
{
|
||||
if(this.IsNoneERIServerSelected)
|
||||
@ -959,6 +974,32 @@ public partial class AssistantERI : AssistantBaseCore
|
||||
}
|
||||
""", true);
|
||||
var fileListAnswer = await this.AddAIResponseAsync(time, true);
|
||||
|
||||
// Is this an update of the ERI server? If so, we need to delete the previously generated files:
|
||||
if (this.writeToFilesystem && this.previouslyGeneratedFiles.Count > 0 && !string.IsNullOrWhiteSpace(fileListAnswer))
|
||||
{
|
||||
foreach (var file in this.previouslyGeneratedFiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(file))
|
||||
{
|
||||
File.Delete(file);
|
||||
this.Logger.LogInformation($"The previously created file '{file}' was deleted.");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Logger.LogWarning($"The previously created file '{file}' could not be found.");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.Logger.LogWarning($"The previously created file '{file}' could not be deleted: {e.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var generatedFiles = new List<string>();
|
||||
foreach (var file in this.ExtractFiles(fileListAnswer))
|
||||
{
|
||||
this.Logger.LogInformation($"The LLM want to create the file: '{file}'");
|
||||
@ -978,9 +1019,69 @@ public partial class AssistantERI : AssistantBaseCore
|
||||
content of the file
|
||||
```
|
||||
""", true);
|
||||
await this.AddAIResponseAsync(time);
|
||||
var generatedCodeMarkdown = await this.AddAIResponseAsync(time);
|
||||
if (this.writeToFilesystem)
|
||||
{
|
||||
var desiredFilePath = Path.Join(this.baseDirectory, file);
|
||||
|
||||
// Security check: ensure that the desired file path is inside the base directory.
|
||||
// We cannot trust the beginning of the file path because it would be possible
|
||||
// to escape by using `..` in the file path.
|
||||
if (!desiredFilePath.StartsWith(this.baseDirectory, StringComparison.InvariantCultureIgnoreCase) || desiredFilePath.Contains(".."))
|
||||
this.Logger.LogWarning($"The file path '{desiredFilePath}' is may not inside the base directory '{this.baseDirectory}'.");
|
||||
|
||||
else
|
||||
{
|
||||
var code = this.ExtractCode(generatedCodeMarkdown);
|
||||
if (string.IsNullOrWhiteSpace(code))
|
||||
this.Logger.LogWarning($"The file content for '{desiredFilePath}' is empty or was not found.");
|
||||
|
||||
else
|
||||
{
|
||||
|
||||
// Ensure that the directory exists:
|
||||
var fileDirectory = Path.GetDirectoryName(desiredFilePath);
|
||||
if (fileDirectory is null)
|
||||
this.Logger.LogWarning($"The file path '{desiredFilePath}' does not contain a directory.");
|
||||
|
||||
else
|
||||
{
|
||||
generatedFiles.Add(desiredFilePath);
|
||||
var fileDirectoryInfo = new DirectoryInfo(fileDirectory);
|
||||
if(!fileDirectoryInfo.Exists)
|
||||
{
|
||||
fileDirectoryInfo.Create();
|
||||
this.Logger.LogInformation($"The directory '{fileDirectory}' was created.");
|
||||
}
|
||||
|
||||
// Save the file to the file system:
|
||||
await File.WriteAllTextAsync(desiredFilePath, code, Encoding.UTF8);
|
||||
this.Logger.LogInformation($"The file '{desiredFilePath}' was created.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(this.writeToFilesystem)
|
||||
{
|
||||
this.previouslyGeneratedFiles = generatedFiles;
|
||||
this.selectedERIServer!.PreviouslyGeneratedFiles = generatedFiles;
|
||||
await this.SettingsManager.StoreSettings();
|
||||
}
|
||||
|
||||
//
|
||||
// ---------------------------------
|
||||
// Ask the AI for further steps
|
||||
// ---------------------------------
|
||||
//
|
||||
time = this.AddUserRequest("""
|
||||
Thank you for implementing the files. Please explain what the next steps are.
|
||||
The goal is for the code to compile and the server to start. We assume that
|
||||
the developer has installed the compiler. We will not consider DevOps tools
|
||||
like Docker.
|
||||
""", true);
|
||||
await this.AddAIResponseAsync(time);
|
||||
await this.SendToAssistant(Tools.Components.CHAT, default);
|
||||
}
|
||||
}
|
@ -99,10 +99,15 @@ public sealed class DataERIServer
|
||||
/// <summary>
|
||||
/// Do you want to write all generated code to the filesystem?
|
||||
/// </summary>
|
||||
public bool WriteToFilesystem { get; set; } = false;
|
||||
public bool WriteToFilesystem { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The base directory where to write the generated code to.
|
||||
/// </summary>
|
||||
public string BaseDirectory { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// We save which files were generated previously.
|
||||
/// </summary>
|
||||
public List<string> PreviouslyGeneratedFiles { get; set; } = new();
|
||||
}
|
Loading…
Reference in New Issue
Block a user