Skip to content

Commit

Permalink
WIP: Self-hosted Code completion for WASM UI
Browse files Browse the repository at this point in the history
  • Loading branch information
highbyte committed Oct 2, 2024
1 parent e1ac801 commit 8077d13
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 11 deletions.
2 changes: 1 addition & 1 deletion doc/SYSTEMS_C64_AI_CODE_COMPLETION.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ If you have your own OpenAI API key, you can connect to OpenAI directly.
- Set `OpenAI API key` to the OpenAI API key.
- Press `Test` to verify that OpenAI API key works against OpenAI API.

#### AI backend type: `LocalOpenAICompatible`
#### AI backend type: `SelfHostedOpenAICompatible`
TODO

#### AI backend type: `None`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,11 @@ public static async Task<ICodeSuggestion> GetCodeSuggestionImplementation(C64Hos
var openAIApiConfig = await GetOpenAIConfig(localStorageService);
codeSuggestion = new OpenAICodeSuggestion(openAIApiConfig, C64BasicCodingAssistant.CODE_COMPLETION_LANGUAGE_DESCRIPTION);
}
else if (c64HostConfig.CodeSuggestionBackendType == CodeSuggestionBackendTypeEnum.SelfHostedOpenAICompatible)
{
var openAIApiConfig = await GetSelfHostedOpenAICompatibleConfig(localStorageService);
codeSuggestion = new OpenAICodeSuggestion(openAIApiConfig, C64BasicCodingAssistant.CODE_COMPLETION_LANGUAGE_DESCRIPTION);
}
else if (c64HostConfig.CodeSuggestionBackendType == CodeSuggestionBackendTypeEnum.CustomEndpoint)
{
var customAIEndpointConfig = await GetCustomAIEndpointConfig(localStorageService);
Expand Down Expand Up @@ -255,12 +260,40 @@ public static async Task<ApiConfig> GetOpenAIConfig(ILocalStorageService localSt
return apiConfig;
}

public static async Task<ApiConfig> GetSelfHostedOpenAICompatibleConfig(ILocalStorageService localStorageService)
{
var apiKey = await localStorageService.GetItemAsStringAsync($"{ApiConfig.CONFIG_SECTION_SELF_HOSTED}:ApiKey");
var deploymentName = await localStorageService.GetItemAsStringAsync($"{ApiConfig.CONFIG_SECTION_SELF_HOSTED}:DeploymentName");
if (string.IsNullOrEmpty(deploymentName))
deploymentName = "stable-code:3b-code-q4_0"; // Default to a Ollama model that is optimized for code completion
var endpoint = await localStorageService.GetItemAsStringAsync($"{ApiConfig.CONFIG_SECTION_SELF_HOSTED}:Endpoint");
if (string.IsNullOrEmpty(endpoint))
endpoint = "http://localhost:11434/api"; // Default to local Ollama
Uri.TryCreate(endpoint, UriKind.Absolute, out var endPointUri);

var apiConfig = new ApiConfig()
{
ApiKey = apiKey, // Optional for Self-hosted model.
DeploymentName = deploymentName, // AI model name (ex: stable-code:3b-code-q4_0)
Endpoint = endPointUri, // Self-hosted OpenAI API compatible endpoint (for example Ollama)
SelfHosted = true // Set to true to use self-hosted OpenAI API compatible endpoint.
};
return apiConfig;
}


public static async Task SaveOpenAICodingAssistantConfigToLocalStorage(ILocalStorageService localStorageService, ApiConfig apiConfig)
{
await localStorageService.SetItemAsStringAsync($"{ApiConfig.CONFIG_SECTION}:ApiKey", apiConfig.ApiKey);
await localStorageService.SetItemAsStringAsync($"{ApiConfig.CONFIG_SECTION}:DeploymentName", apiConfig.DeploymentName);
await localStorageService.SetItemAsStringAsync($"{ApiConfig.CONFIG_SECTION}:Endpoint", apiConfig.Endpoint != null ? apiConfig.Endpoint.OriginalString : "");
await localStorageService.SetItemAsync($"{ApiConfig.CONFIG_SECTION}:SelfHosted", apiConfig.SelfHosted);
}

public static async Task SaveSelfHostedOpenAICompatibleCodingAssistantConfigToLocalStorage(ILocalStorageService localStorageService, ApiConfig apiConfig)
{
await localStorageService.SetItemAsStringAsync($"{ApiConfig.CONFIG_SECTION_SELF_HOSTED}:ApiKey", !string.IsNullOrEmpty(apiConfig.ApiKey) ? apiConfig.ApiKey : string.Empty);
await localStorageService.SetItemAsStringAsync($"{ApiConfig.CONFIG_SECTION_SELF_HOSTED}:DeploymentName", !string.IsNullOrEmpty(apiConfig.DeploymentName) ? apiConfig.DeploymentName : string.Empty);
await localStorageService.SetItemAsStringAsync($"{ApiConfig.CONFIG_SECTION_SELF_HOSTED}:Endpoint", apiConfig.Endpoint != null ? apiConfig.Endpoint.OriginalString : "");
}

public static async Task<CustomAIEndpointConfig> GetCustomAIEndpointConfig(ILocalStorageService localStorageService)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,40 @@
</div>
</div>
}

@if (@C64HostConfig.CodeSuggestionBackendType == CodeSuggestionBackendTypeEnum.SelfHostedOpenAICompatible)
{
<div class="table-row">
<div class="table-cell table-cell-fixed-width-medium twocol">Self-hosted OpenAI compatible endpoint (ex: Ollama on http://localhost:11434/api)</div>
<div class="table-cell table-cell-fixed-width-large twocol">
@if (_selfHostedOpenAICompatibleAIApiConfig != null)
{
@* <InputText TValue="string" @bind-Value:get="this._selfHostedOpenAICompatibleAIApiConfig.Endpoint.ToString()" @bind-Value:set="((string e) => this._selfHostedOpenAICompatibleAIApiConfig.Endpoint = new Uri(e))" style="width: inherit" /> *@
}
</div>
</div>

<div class="table-row">
<div class="table-cell table-cell-fixed-width-medium twocol">Model name (ex: stable-code:3b-code-q4_0)</div>
<div class="table-cell table-cell-fixed-width-large twocol">
@if (_selfHostedOpenAICompatibleAIApiConfig != null)
{
<InputText @ref="_selfHostedOpenAICompatibleModelNameInputText" @bind-Value="_selfHostedOpenAICompatibleAIApiConfig.DeploymentName" style="width: inherit" />
}
</div>
</div>

<div class="table-row">
<div class="table-cell table-cell-fixed-width-medium twocol">Self-hosted API key (optional)</div>
<div class="table-cell table-cell-fixed-width-large twocol">
@if (_selfHostedOpenAICompatibleAIApiConfig != null)
{
<InputText @ref="_selfHostedOpenAICompatibleApiKeyInputText" @bind-Value="_selfHostedOpenAICompatibleAIApiConfig.ApiKey" style="width: inherit" />
}
</div>
</div>
}

@if (@C64HostConfig.CodeSuggestionBackendType == CodeSuggestionBackendTypeEnum.CustomEndpoint)
{
<div class="table-row">
Expand All @@ -226,15 +260,19 @@
</div>
</div>
}
<div class="table-row">
<div class="table-cell table-cell-fixed-width-medium twocol"><button @onclick="TestAIAssistantEndpoint">Test</button></div>
<div class="table-cell twocol" style="word-break: break-word">
@if (!string.IsNullOrEmpty(_aiBackendValidationMessage))
{
@_aiBackendValidationMessage
}

@if (@C64HostConfig.CodeSuggestionBackendType != CodeSuggestionBackendTypeEnum.None)
{
<div class="table-row">
<div class="table-cell table-cell-fixed-width-medium twocol"><button @onclick="TestAIAssistantEndpoint">Test</button></div>
<div class="table-cell twocol" style="word-break: break-word">
@if (!string.IsNullOrEmpty(_aiBackendValidationMessage))
{
@_aiBackendValidationMessage
}
</div>
</div>
</div>
}
</div>
</div>
</div>
Expand All @@ -261,6 +299,7 @@
protected override async Task OnInitializedAsync()
{
_openAIApiConfig = await C64Setup.GetOpenAIConfig(LocalStorage);
_selfHostedOpenAICompatibleAIApiConfig = await C64Setup.GetSelfHostedOpenAICompatibleConfig(LocalStorage);
_customEndpointAIApiConfig = await C64Setup.GetCustomAIEndpointConfig(LocalStorage);
BlazoredModal.SetTitle("C64 config");

Expand All @@ -274,6 +313,11 @@
private ApiConfig _openAIApiConfig = default!;
private InputText _openAIApiKeyInputText = default!;

private ApiConfig _selfHostedOpenAICompatibleAIApiConfig = default!;
private InputText _selfHostedOpenAICompatibleEndpointInputText = default!;
private InputText _selfHostedOpenAICompatibleModelNameInputText = default!;
private InputText _selfHostedOpenAICompatibleApiKeyInputText = default!;

private CustomAIEndpointConfig _customEndpointAIApiConfig = default!;
private InputText _customEndpointAIApiKeyInputText = default!;

Expand All @@ -283,6 +327,10 @@
{
await C64Setup.SaveOpenAICodingAssistantConfigToLocalStorage(LocalStorage, _openAIApiConfig);
}
if (C64HostConfig.CodeSuggestionBackendType == CodeSuggestionBackendTypeEnum.SelfHostedOpenAICompatible)
{
await C64Setup.SaveSelfHostedOpenAICompatibleCodingAssistantConfigToLocalStorage(LocalStorage, _selfHostedOpenAICompatibleAIApiConfig);
}
else if (C64HostConfig.CodeSuggestionBackendType == CodeSuggestionBackendTypeEnum.CustomEndpoint)
{
await C64Setup.SaveCustomCodingAssistantConfigToLocalStorage(LocalStorage, _customEndpointAIApiConfig);
Expand Down Expand Up @@ -387,6 +435,10 @@
{
codeSuggestion = new OpenAICodeSuggestion(_openAIApiConfig, C64BasicCodingAssistant.CODE_COMPLETION_LANGUAGE_DESCRIPTION);
}
else if(C64HostConfig.CodeSuggestionBackendType == CodeSuggestionBackendTypeEnum.SelfHostedOpenAICompatible)
{
codeSuggestion = new OpenAICodeSuggestion(_selfHostedOpenAICompatibleAIApiConfig, C64BasicCodingAssistant.CODE_COMPLETION_LANGUAGE_DESCRIPTION);
}
else if(C64HostConfig.CodeSuggestionBackendType == CodeSuggestionBackendTypeEnum.CustomEndpoint)
{
codeSuggestion = new CustomAIEndpointCodeSuggestion(_customEndpointAIApiConfig, C64BasicCodingAssistant.CODE_COMPLETION_LANGUAGE_DESCRIPTION);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ public enum CodeSuggestionBackendTypeEnum
{
None,
OpenAI,
SelfHostedOpenAICompatible,
CustomEndpoint
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public static ICodeSuggestion CreateCodeSuggestion(
{
CodeSuggestionBackendTypeEnum.OpenAI => new OpenAICodeSuggestion(configuration, programmingLanguage),
CodeSuggestionBackendTypeEnum.CustomEndpoint => new CustomAIEndpointCodeSuggestion(configuration, programmingLanguage),
CodeSuggestionBackendTypeEnum.SelfHostedOpenAICompatible => new OpenAICodeSuggestion(configuration, programmingLanguage),
CodeSuggestionBackendTypeEnum.None => new NoCodeSuggestion(),
_ => throw new NotImplementedException($"CodeSuggestionBackendType '{codeSuggestionBackendType}' is not implemented.")
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class ApiConfig
public bool SelfHosted { get; set; }

public const string CONFIG_SECTION = "CodingAssistant:OpenAI";
public const string CONFIG_SECTION_SELF_HOSTED = "CodingAssistant:SelfHostedOpenAICompatible";

public ApiConfig()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Azure.AI.OpenAI" Version="1.0.0-beta.11" />
<PackageReference Include="Azure.AI.OpenAI" Version="1.0.0-beta.17" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" />

</ItemGroup>
Expand Down

0 comments on commit 8077d13

Please sign in to comment.