Skip to content

Commit

Permalink
WIP: Better SadConsole implementation for C64 code assistant AI. Cust…
Browse files Browse the repository at this point in the history
…om assistant endpoint not requiring OpenAI key on client.
  • Loading branch information
highbyte committed Sep 20, 2024
1 parent 0cae65a commit 6fc9bee
Show file tree
Hide file tree
Showing 21 changed files with 476 additions and 85 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using Highbyte.DotNet6502.AI.CodingAssistant;
using Highbyte.DotNet6502.App.SadConsole.SystemSetup;
using Highbyte.DotNet6502.Systems.Commodore64.Config;
using Highbyte.DotNet6502.Systems.Commodore64.Utils.BasicAssistant;
using Highbyte.DotNet6502.Utils;
using Microsoft.Extensions.Configuration;
using SadConsole.UI;
using SadConsole.UI.Controls;
using SadRogue.Primitives;
Expand All @@ -11,12 +14,13 @@ public class C64ConfigUIConsole : Window
public const int CONSOLE_WIDTH = USABLE_WIDTH;
public const int CONSOLE_HEIGHT = USABLE_HEIGHT;
private const int USABLE_WIDTH = 60;
private const int USABLE_HEIGHT = 25;
private const int USABLE_HEIGHT = 28;

public readonly C64Config C64Config;
public readonly C64HostConfig C64HostConfig;
private readonly IConfiguration _configuration;

public C64ConfigUIConsole(SadConsoleHostApp sadConsoleHostApp) : base(CONSOLE_WIDTH, CONSOLE_HEIGHT)
public C64ConfigUIConsole(SadConsoleHostApp sadConsoleHostApp, IConfiguration configuration) : base(CONSOLE_WIDTH, CONSOLE_HEIGHT)
{

C64Config = (C64Config)sadConsoleHostApp.CurrentSystemConfig.Clone();
Expand All @@ -37,6 +41,7 @@ public C64ConfigUIConsole(SadConsoleHostApp sadConsoleHostApp) : base(CONSOLE_WI
UseKeyboard = true;

DrawUIItems();
_configuration = configuration;
}

private void DrawUIItems()
Expand Down Expand Up @@ -133,9 +138,66 @@ private void DrawUIItems()
openROMDownloadURLButton.Click += (s, e) => OpenURL(romDownloadLinkTextBox.Text);
Controls.Add(openROMDownloadURLButton);

// AI coding assistant selection
var codingAssistantLabel = CreateLabel("Basic AI assistant: ", 1, romDownloadLinkTextBox.Bounds.MaxExtentY + 3);
var codingAssistantValue = CreateLabel($"{C64HostConfig.CodeSuggestionBackendType}", codingAssistantLabel.Bounds.MaxExtentX + 1, codingAssistantLabel.Position.Y);

var codingAssistantInfoLabel = new Label(Width - 10)
{
Name = "codingAssistantInfoLabel",
Position = (1, codingAssistantValue.Bounds.MaxExtentY + 1),
IsEnabled = false,
DisplayText = "Set AI assistant in appsetting.json",
};
Controls.Add(codingAssistantInfoLabel);

var codingAssistantTestButton = new Button("Test")
{
Name = "codingAssistantTestButton",
Position = (codingAssistantValue.Bounds.MaxExtentX + 2, codingAssistantValue.Position.Y),
};
codingAssistantTestButton.Click += async (s, e) =>
{
try
{
var codeSuggestionBackend = CodeSuggestionConfigurator.CreateCodeSuggestion(C64HostConfig.CodeSuggestionBackendType, _configuration, C64BasicCodingAssistant.CODE_COMPLETION_LANGUAGE_DESCRIPTION);
await codeSuggestionBackend.CheckAvailability();
if (codeSuggestionBackend.IsAvailable)
{
codingAssistantInfoLabel.DisplayText = "OK";
codingAssistantInfoLabel.TextColor = Color.Green;
}
else
{
codingAssistantInfoLabel.DisplayText = codeSuggestionBackend.LastError ?? "Error";
codingAssistantInfoLabel.TextColor = Color.Red;
}
}
catch (Exception ex)
{
codingAssistantInfoLabel.DisplayText = ex.Message;
codingAssistantInfoLabel.TextColor = Color.Red;
}
};
Controls.Add(codingAssistantTestButton);


//ComboBox codingAssistantComboBox = new ComboBox(codingAssistantLabel.Bounds.MaxExtentX + 1, codingAssistantLabel.Position.Y, 6, Enum.GetNames<CodeSuggestionBackendTypeEnum>().ToArray())
//{
// Position = (codingAssistantLabel.Bounds.MaxExtentX + 2, codingAssistantLabel.Position.Y),
// Name = "codingAssistantComboBox",
// SelectedItem = C64HostConfig.CodeSuggestionBackendType.ToString(),
//};
//codingAssistantComboBox.SelectedItemChanged += (s, e) =>
//{
// C64HostConfig.CodeSuggestionBackendType = (CodeSuggestionBackendTypeEnum)codingAssistantComboBox.SelectedItem;
// IsDirty = true;
//};
//Controls.Add(codingAssistantComboBox);


// Validaton errors
var validationErrorsLabel = CreateLabel("Validation errors", 1, romDownloadLinkTextBox.Bounds.MaxExtentY + 2, "validationErrorsLabel");
var validationErrorsLabel = CreateLabel("Validation errors", 1, codingAssistantInfoLabel.Bounds.MaxExtentY + 2, "validationErrorsLabel");
var validationErrorsListBox = new ListBox(Width - 3, 3)
{
Name = "validationErrorsListBox",
Expand Down Expand Up @@ -245,6 +307,9 @@ private void SetControlStates()
chargenROMTextBox!.Text = C64Config.ROMs.SingleOrDefault(x => x.Name == C64Config.CHARGEN_ROM_NAME).File;
chargenROMTextBox!.IsDirty = true;

var codingAssistantTestButton = Controls["codingAssistantTestButton"] as Button;
codingAssistantTestButton.IsEnabled = C64HostConfig.CodeSuggestionBackendType != CodeSuggestionBackendTypeEnum.None;

var isOk = C64Config.IsValid(out List<string> validationErrors);
var okButton = Controls["okButton"] as Button;
okButton!.IsEnabled = isOk;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using TextCopy;
using Highbyte.DotNet6502.Impl.SadConsole.Commodore64.Input;
using Highbyte.DotNet6502.App.SadConsole.SystemSetup;
using Microsoft.Extensions.Configuration;

namespace Highbyte.DotNet6502.App.SadConsole.ConfigUI;
public class C64MenuConsole : ControlsConsole
Expand All @@ -18,13 +19,19 @@ public class C64MenuConsole : ControlsConsole
private const int USABLE_HEIGHT = 10;

private readonly SadConsoleHostApp _sadConsoleHostApp;
private readonly IConfiguration _configuration;
private readonly ILogger _logger;

private C64SadConsoleInputHandler C64SadConsoleInputHandler => (C64SadConsoleInputHandler)_sadConsoleHostApp.CurrentSystemRunner.InputHandler;

public C64MenuConsole(SadConsoleHostApp sadConsoleHostApp, ILoggerFactory loggerFactory) : base(CONSOLE_WIDTH, CONSOLE_HEIGHT)
public C64MenuConsole(
SadConsoleHostApp sadConsoleHostApp,
ILoggerFactory loggerFactory,
IConfiguration configuration
) : base(CONSOLE_WIDTH, CONSOLE_HEIGHT)
{
_sadConsoleHostApp = sadConsoleHostApp;
_configuration = configuration;
_logger = loggerFactory.CreateLogger(typeof(C64MenuConsole).Name);

Controls.ThemeColors = SadConsoleUISettings.ThemeColors;
Expand Down Expand Up @@ -87,9 +94,9 @@ private void DrawUIItems()
Name = "c64aiBasicAssistantCheckbox",
Position = (1, c64CopyBasicSourceCodeButton.Bounds.MaxExtentY + 1),
};
c64aiBasicAssistantCheckbox.IsSelectedChanged += (s, e) =>
c64aiBasicAssistantCheckbox.IsSelectedChanged += async (s, e) =>
{
SetBasicAIAssistant(c64aiBasicAssistantCheckbox.IsSelected);
await SetBasicAIAssistant(c64aiBasicAssistantCheckbox.IsSelected);
};
Controls.Add(c64aiBasicAssistantCheckbox);

Expand Down Expand Up @@ -215,7 +222,7 @@ private void C64SaveBasicButton_Click(object? sender, EventArgs e)

private void C64ConfigButton_Click(object sender, EventArgs e)
{
var window = new C64ConfigUIConsole(_sadConsoleHostApp);
var window = new C64ConfigUIConsole(_sadConsoleHostApp, _configuration);

window.Center();
window.Closed += (s2, e2) =>
Expand Down Expand Up @@ -294,18 +301,23 @@ private void SetControlStates()
validationMessageValueLabel!.IsVisible = !isOk;
}

public void ToggleBasicAIAssistant()
public async Task ToggleBasicAIAssistant()
{
var c64aiBasicAssistantCheckbox = Controls["c64aiBasicAssistantCheckbox"] as CheckBox;
SetBasicAIAssistant(!c64aiBasicAssistantCheckbox.IsSelected);
await SetBasicAIAssistant(!c64aiBasicAssistantCheckbox.IsSelected);
}

private void SetBasicAIAssistant(bool enabled)
private async Task SetBasicAIAssistant(bool enabled)
{
if (_sadConsoleHostApp.EmulatorState != Systems.EmulatorState.Running)
if (_sadConsoleHostApp.EmulatorState != EmulatorState.Running)
return;
C64SadConsoleInputHandler.CodingAssistantEnabled = enabled;
((C64HostConfig)_sadConsoleHostApp.CurrentHostSystemConfig).BasicAIAssistantDefaultEnabled = enabled;
if (enabled)
{
await C64SadConsoleInputHandler.CheckCodingAssistantAvailability();
var test = C64SadConsoleInputHandler.CodingAssistantAvailable;
}
((C64HostConfig)_sadConsoleHostApp.CurrentHostSystemConfig).BasicAIAssistantDefaultEnabled = C64SadConsoleInputHandler.CodingAssistantEnabled;
IsDirty = true;
}
}
2 changes: 1 addition & 1 deletion src/apps/Highbyte.DotNet6502.App.SadConsole/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,5 @@
// ----------
emulatorConfig.Validate(systemList);

var silkNetHostApp = new SadConsoleHostApp(systemList, loggerFactory, emulatorConfig, logStore, logConfig);
var silkNetHostApp = new SadConsoleHostApp(systemList, loggerFactory, emulatorConfig, logStore, logConfig, Configuration);
silkNetHostApp.Run();
32 changes: 14 additions & 18 deletions src/apps/Highbyte.DotNet6502.App.SadConsole/SadConsoleHostApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Highbyte.DotNet6502.Impl.SadConsole;
using Highbyte.DotNet6502.Systems;
using Highbyte.DotNet6502.Systems.Logging.InMem;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using SadConsole.Components;
using SadConsole.Configuration;
Expand All @@ -28,6 +29,7 @@ public class SadConsoleHostApp : HostApp<SadConsoleRenderContext, SadConsoleInpu

private readonly DotNet6502InMemLogStore _logStore;
private readonly DotNet6502InMemLoggerConfiguration _logConfig;
private readonly IConfiguration _configuration;
private readonly ILoggerFactory _loggerFactory;

// --------------------
Expand Down Expand Up @@ -89,19 +91,19 @@ public SadConsoleHostApp(
EmulatorConfig emulatorConfig,
//IWindow window,
DotNet6502InMemLogStore logStore,
DotNet6502InMemLoggerConfiguration logConfig
) : base("SadConsole", systemList, loggerFactory)
DotNet6502InMemLoggerConfiguration logConfig,
IConfiguration configuration)
: base("SadConsole", systemList, loggerFactory)
{
_emulatorConfig = emulatorConfig;
//_window = window;
_emulatorConfig = emulatorConfig;
_logStore = logStore;
_logConfig = logConfig;

_configuration = configuration;
_loggerFactory = loggerFactory;
_logger = loggerFactory.CreateLogger(typeof(SadConsoleHostApp).Name);
}


public void Run()
{
//SetUninitializedWindow();
Expand Down Expand Up @@ -143,7 +145,6 @@ public void Run()
Game.Instance.Dispose();
}


private IScreenObject CreateMainSadConsoleScreen(GameHost gameHost)
{
//ScreenSurface screen = new(gameInstance.ScreenCellsX, gameInstance.ScreenCellsY);
Expand Down Expand Up @@ -236,7 +237,7 @@ public override void OnAfterSelectSystem()
// Create system specific menu console
if (SelectedSystemName == "C64")
{
_systemMenuConsole = new C64MenuConsole(this, _loggerFactory);
_systemMenuConsole = new C64MenuConsole(this, _loggerFactory, _configuration);
_systemMenuConsole.Position = (MENU_POSITION_X, _menuConsole.Height);
_sadConsoleScreen.Children.Add(_systemMenuConsole);
}
Expand Down Expand Up @@ -574,7 +575,7 @@ public void SetEmulatorConsoleFocus()
_sadConsoleEmulatorConsole.IsFocused = true;
}

private void HandleUIKeyboardInput()
private async Task HandleUIKeyboardInput()
{
var keyboard = GameHost.Instance.Keyboard;
//if (keyboard.IsKeyPressed(Keys.F10))
Expand All @@ -583,20 +584,15 @@ private void HandleUIKeyboardInput()
if (keyboard.IsKeyPressed(Keys.F11))
ToggleInfo();

if (EmulatorState == EmulatorState.Running || EmulatorState == EmulatorState.Paused)
if (keyboard.IsKeyPressed(Keys.F12) && (EmulatorState == EmulatorState.Running || EmulatorState == EmulatorState.Paused))
{
if (keyboard.IsKeyPressed(Keys.F12))
ToggleMonitor();
ToggleMonitor();
}

if (EmulatorState == EmulatorState.Running)
if (keyboard.IsKeyPressed(Keys.F9) && EmulatorState == EmulatorState.Running)
{
if (keyboard.IsKeyPressed(Keys.F9))
{
if (_systemMenuConsole is C64MenuConsole c64MenuConsole)
c64MenuConsole.ToggleBasicAIAssistant();
}
if (_systemMenuConsole is C64MenuConsole c64MenuConsole)
await c64MenuConsole.ToggleBasicAIAssistant();
}

}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
using Highbyte.DotNet6502.AI.CodingAssistant;

namespace Highbyte.DotNet6502.App.SadConsole.SystemSetup;

public class C64HostConfig : SadConsoleHostSystemConfigBase
{
public bool BasicAIAssistantDefaultEnabled { get; set; }

public CodeSuggestionBackendTypeEnum CodeSuggestionBackendType { get; set; }

public C64HostConfig()
{
BasicAIAssistantDefaultEnabled = false;
CodeSuggestionBackendType = CodeSuggestionBackendTypeEnum.OpenAI;

//Font = "Fonts/C64.font";
//DefaultFontSize = IFont.Sizes.One;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,12 @@ public C64Setup(ILoggerFactory loggerFactory, IConfiguration configuration)

public IHostSystemConfig GetNewHostSystemConfig()
{
// TODO: Read System host config from appsettings.json
var c64HostConfig = new C64HostConfig { };
// TODO: Read all system host config from appsettings.json
var c64HostConfig = new C64HostConfig
{
BasicAIAssistantDefaultEnabled = false,
CodeSuggestionBackendType = Enum.Parse<CodeSuggestionBackendTypeEnum>(_configuration["CodingAssistant:CodingAssistantType"] ?? "None")
};
return c64HostConfig;
}

Expand Down Expand Up @@ -78,8 +82,8 @@ NAudioAudioHandlerContext audioHandlerContext

var renderer = new C64SadConsoleRenderer(c64, renderContext);

var openAICodeSuggestion = new OpenAICodeSuggestion(_configuration, "Commodore 64 Basic");
var c64BasicCodingAssistant = new C64BasicCodingAssistant(c64, openAICodeSuggestion, _loggerFactory);
ICodeSuggestion codeSuggestion = CodeSuggestionConfigurator.CreateCodeSuggestion(c64HostConfig.CodeSuggestionBackendType, _configuration, C64BasicCodingAssistant.CODE_COMPLETION_LANGUAGE_DESCRIPTION, defaultToNoneIdConfigError: true);
var c64BasicCodingAssistant = new C64BasicCodingAssistant(c64, codeSuggestion, _loggerFactory);
var inputHandler = new C64SadConsoleInputHandler(c64, inputHandlerContext, _loggerFactory, c64BasicCodingAssistant, c64HostConfig.BasicAIAssistantDefaultEnabled);

var audioHandler = new C64NAudioAudioHandler(c64, audioHandlerContext, _loggerFactory);
Expand Down
36 changes: 24 additions & 12 deletions src/apps/Highbyte.DotNet6502.App.SadConsole/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -164,21 +164,33 @@
}
},

"CodingAssistant": {
"CodingAssistantType": "OpenAI", // "None", "OpenAI", "CustomEndpoint"

"OpenAI": {
// Set to true to enable OpenAI Basic coding assistant. Also requires an API key (see below).
"Enabled": false,
"OpenAI": {
// Set to true to enable OpenAI Basic coding assistant. Also requires an API key (see below).
"Enabled": false,

// dotnet user-secrets set "OpenAI:ApiKey" "[MY API KEY]"
"ApiKey": "[SET IN DOTNET USER SECRETS]",
// dotnet user-secrets set "CodingAssistant:OpenAI:ApiKey" "[MY API KEY]"
"ApiKey": "[SET IN DOTNET USER SECRETS]",

//"DeploymentName": "gpt-3.5-turbo", // Don't work
//"DeploymentName": "gpt-4-turbo" // Works somewhat
//"DeploymentName": "gpt-4o-mini" // Works a bit better sometimes?
"DeploymentName": "gpt-4o" // Works good
//"DeploymentName": "chatgpt-4o-latest" // Works good
//"DeploymentName": "gpt-3.5-turbo", // Don't work
//"DeploymentName": "gpt-4-turbo" // Works somewhat
//"DeploymentName": "gpt-4o-mini" // Works a bit better sometimes?
"DeploymentName": "gpt-4o" // Works good
//"DeploymentName": "chatgpt-4o-latest" // Works good

// Required for Azure OpenAI only. If you're using OpenAI, remove the following line.
//"Endpoint": "https://YOUR_ACCOUNT.openai.azure.com/"
},

"CustomEndpoint": {
// Set to true to use special endpoint that encapsulates code completion requests and forwards to OpenAI API (no OpenAI key is required on client, instead a API key for the custom endpoint)
"Endpoint": "",

// dotnet user-secrets set "CodingAssistant:CustomEndpoint:ApiKey" "[MY API KEY]"
"ApiKey": "[SET IN DOTNET USER SECRETS]"
}

// Required for Azure OpenAI only. If you're using OpenAI, remove the following line.
//"Endpoint": "https://YOUR_ACCOUNT.openai.azure.com/"
}
}
Loading

0 comments on commit 6fc9bee

Please sign in to comment.