feat: more work on getting console client sorted

This commit is contained in:
Stevan Freeborn
2025-02-10 15:31:25 -06:00
parent 92c0bee83b
commit 4672644127
3 changed files with 167 additions and 5 deletions
+3
View File
@@ -9,8 +9,11 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="9.1.0" />
<PackageReference Include="Spectre.Console" Version="0.49.1" />
<PackageReference Include="Spectre.Console.Cli" Version="0.49.1" />
<PackageReference Include="TestableIO.System.IO.Abstractions.Wrappers" Version="21.3.1" />
</ItemGroup>
</Project>
@@ -0,0 +1,11 @@
namespace AltGen.Console.Common;
static class HostBuilderExtensions
{
public static CommandApp<GenerateCommand> BuildApp(this IHostBuilder builder)
{
var registrar = new TypeRegistrar(builder);
var app = new CommandApp<GenerateCommand>(registrar);
return app;
}
}
+153 -5
View File
@@ -4,11 +4,159 @@
// and then post the image to our API
// get the response and display the alt text
using Microsoft.Extensions.Hosting;
using System.ComponentModel;
using System.IO.Abstractions;
using System.Net.Http.Headers;
using AltGen.Console.Common;
using Spectre.Console;
await Host.CreateDefaultBuilder(args)
.ConfigureServices(static (_, services) => { })
.Build() // TODO: Need special extension for integrating host with Spectre.Console
.RunAsync();
.ConfigureServices(static (_, services) =>
{
services.AddSingleton<IFileSystem, FileSystem>();
services.AddSingleton(AnsiConsole.Console);
services.AddHttpClient<IAltGenService, AltGenService>()
.AddStandardResilienceHandler();
})
.BuildApp()
.RunAsync(args);
Console.WriteLine("Hello, World!");
sealed class GenerateCommand(
IAnsiConsole console,
IFileSystem fileSystem,
IAltGenService altGenService
) : AsyncCommand<GenerateCommand.Settings>
{
readonly IAnsiConsole _console = console;
readonly IFileSystem _fileSystem = fileSystem;
readonly IAltGenService _altGenService = altGenService;
public class Settings(IFileSystem fileSystem) : CommandSettings
{
readonly Dictionary<string, string> _imageTypes = new()
{
[".jpeg"] = "image/jpeg",
[".jpg"] = "image/jpeg",
[".png"] = "image/png"
};
readonly List<string> _providers = [
"gemini",
];
readonly IFileSystem _fileSystem = fileSystem;
[CommandArgument(1, "<provider>")]
[Description("The provider to use for generating alt text.")]
public string Provider { get; init; } = string.Empty;
[CommandArgument(2, "<key>")]
[Description("The key for the provider.")]
public string Key { get; init; } = string.Empty;
[CommandArgument(3, "<path>")]
[Description("The path to the image to generate alt text for.")]
public string Path { get; init; } = string.Empty;
public string ContentType => _imageTypes[_fileSystem.Path.GetExtension(Path)];
public override ValidationResult Validate()
{
if (_providers.Contains(Provider) is false)
{
return ValidationResult.Error($"The provider '{Provider}' is not supported.");
}
var pathExists = _fileSystem.File.Exists(Path);
if (pathExists is false)
{
return ValidationResult.Error($"The path '{Path}' does not exist.");
}
var extension = _fileSystem.Path.GetExtension(Path);
if (_imageTypes.ContainsKey(extension) is false)
{
return ValidationResult.Error($"The file '{Path}' is not a valid image file.");
}
return ValidationResult.Success();
}
}
public override async Task<int> ExecuteAsync(CommandContext context, Settings settings)
{
var fileName = _fileSystem.Path.GetFileName(settings.Path);
var image = await _fileSystem.File.ReadAllBytesAsync(settings.Path);
var altText = await _altGenService.GenerateAltTextAsync(
settings.Provider,
settings.Key,
fileName,
image,
settings.ContentType
);
_console.MarkupLine($"[bold]Alt Text:[/] {altText}");
return 0;
}
}
interface IAltGenService
{
Task<string> GenerateAltTextAsync(
string provider,
string key,
string fileName,
byte[] image,
string contentType
);
}
sealed class AltGenService(HttpClient client) : IAltGenService
{
readonly HttpClient _client = client;
public async Task<string> GenerateAltTextAsync(
string provider,
string key,
string fileName,
byte[] image,
string contentType
)
{
var byteContent = new ByteArrayContent(image);
byteContent.Headers.ContentType = new MediaTypeHeaderValue(contentType);
// TODO: Need to have proper URL
// have constants generated at build time
// that point to proper API URL
var request = new HttpRequestMessage(HttpMethod.Post, "altgen")
{
Content = new MultipartFormDataContent
{
{ new StringContent(provider), "provider" },
{ new StringContent(key), "providerKey" },
{ new ByteArrayContent(image), "file", fileName }
}
};
var response = await _client.SendAsync(request);
if (response.IsSuccessStatusCode is false)
{
// TODO: Use accurate exception
throw new InvalidDataException("Failed to generate alt text.");
}
// TODO: Deserialize the response
var altText = await response.Content.ReadAsStringAsync();
return altText;
}
}