Skip to content

Commit 1cb4213

Browse files
committed
Leverage DI for command registration too
This attribute-based approach allows tests to leverage DI to replace the IAnsiConsole too. It should also make console startup faster since resolving the type ctor is done at compile-time.
1 parent 418f330 commit 1cb4213

24 files changed

+78
-10
lines changed

src/dotnet-openai/App.cs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
using Devlooped.Sponsors;
1+
using System.Diagnostics.CodeAnalysis;
2+
using Devlooped.Sponsors;
23
using GitCredentialManager;
34
using Microsoft.Extensions.Configuration;
45
using Microsoft.Extensions.DependencyInjection;
56
using OpenAI;
7+
using Spectre.Console;
68
using Spectre.Console.Cli;
79

810
namespace Devlooped.OpenAI;
@@ -13,12 +15,28 @@ public static class App
1315

1416
static App() => Console.CancelKeyPress += (s, e) => shutdownCancellation.Cancel();
1517

18+
/// <summary>
19+
/// Whether the CLI app is not interactive (i.e. part of a script run,
20+
/// running in CI, or in a non-interactive user session).
21+
/// </summary>
22+
public static bool IsNonInteractive => !Environment.UserInteractive
23+
|| Console.IsInputRedirected
24+
|| Console.IsOutputRedirected;
25+
1626
public static CommandApp Create() => Create(out _);
1727

18-
public static CommandApp Create(out IServiceProvider services)
28+
public static CommandApp Create([NotNull] out IServiceProvider services)
29+
=> Create(new ServiceCollection(), out services);
30+
31+
public static CommandApp Create(IAnsiConsole console, [NotNull] out IServiceProvider services)
1932
{
20-
var collection = new ServiceCollection();
33+
var app = Create(new ServiceCollection().AddSingleton(console), out services);
34+
app.Configure(config => config.ConfigureConsole(console));
35+
return app;
36+
}
2137

38+
static CommandApp Create(IServiceCollection collection, [NotNull] out IServiceProvider services)
39+
{
2240
var config = new ConfigurationBuilder()
2341
.AddEnvironmentVariables()
2442
#if DEBUG
@@ -45,6 +63,7 @@ public static CommandApp Create(out IServiceProvider services)
4563
});
4664

4765
collection.ConfigureSponsors();
66+
collection.AddServices();
4867

4968
var registrar = new TypeRegistrar(collection);
5069
var app = new CommandApp(registrar);

src/dotnet-openai/Auth/LoginCommand.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using System.ComponentModel;
2-
using System.ComponentModel.DataAnnotations;
32
using GitCredentialManager;
3+
using Microsoft.Extensions.DependencyInjection;
44
using Spectre.Console;
55
using Spectre.Console.Cli;
66

@@ -19,6 +19,7 @@ Switch easily between keys by just specifying the project name after initial log
1919
2020
For example, to use {{{ThisAssembly.Project.ToolCommandName}}} in GitHub Actions, add `OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}` to "env".
2121
""")]
22+
[Service]
2223
public class LoginCommand(IAnsiConsole console, ICredentialStore store) : Command<LoginCommand.LoginSettings>
2324
{
2425
public override int Execute(CommandContext context, LoginSettings settings)

src/dotnet-openai/Auth/LogoutCommand.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
using System.ComponentModel;
22
using GitCredentialManager;
3+
using Microsoft.Extensions.DependencyInjection;
34
using Spectre.Console;
45
using Spectre.Console.Cli;
56

67
namespace Devlooped.OpenAI.Auth;
78

89
[Description("Log out of api.openai.com")]
10+
[Service]
911
class LogoutCommand(ICredentialStore store, IAnsiConsole console) : Command
1012
{
1113
public override int Execute(CommandContext context)

src/dotnet-openai/Auth/StatusCommand.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
using System.ComponentModel;
33
using GitCredentialManager;
44
using Microsoft.Extensions.Configuration;
5+
using Microsoft.Extensions.DependencyInjection;
56
using OpenAI;
67
using Spectre.Console;
78
using Spectre.Console.Cli;
89

910
namespace Devlooped.OpenAI.Auth;
1011

1112
[Description("Shows the current authentication status")]
13+
[Service]
1214
class StatusCommand(IAnsiConsole console, IConfiguration configuration, ICredentialStore store, OpenAIClient client) : AsyncCommand<StatusCommand.StatusSettings>
1315
{
1416
public override async Task<int> ExecuteAsync(CommandContext context, StatusSettings settings)

src/dotnet-openai/Auth/TokenCommand.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
using System.ComponentModel;
22
using GitCredentialManager;
33
using Microsoft.Extensions.Configuration;
4+
using Microsoft.Extensions.DependencyInjection;
45
using Spectre.Console;
56
using Spectre.Console.Cli;
67

78
namespace Devlooped.OpenAI.Auth;
89

910
[Description($"Print the auth token {ThisAssembly.Project.ToolCommandName} is configured to use")]
11+
[Service]
1012
class TokenCommand(IConfiguration configuration, ICredentialStore store, IAnsiConsole console) : Command
1113
{
1214
public override int Execute(CommandContext context)

src/dotnet-openai/Docs/vector.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ COMMANDS:
1717
modify <ID> Modify a vector store
1818
delete <ID> Delete a vector store by ID
1919
list List vector stores
20-
view <ID> View a store by its ID
20+
view View a store by its ID
2121
search <ID> <QUERY> Performs semantic search against a vector store
2222
file Vector store files operations
2323
```

src/dotnet-openai/File/DeleteCommand.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
using System.ComponentModel;
2+
using Microsoft.Extensions.DependencyInjection;
23
using OpenAI;
34
using Spectre.Console;
45
using Spectre.Console.Cli;
56

67
namespace Devlooped.OpenAI.File;
78

89
[Description("Delete a file by its ID.")]
10+
[Service]
911
class DeleteCommand(OpenAIClient oai, IAnsiConsole console, CancellationTokenSource cts) : AsyncCommand<DeleteCommand.DeleteSettings>
1012
{
1113
public override async Task<int> ExecuteAsync(CommandContext context, DeleteSettings settings)
@@ -17,7 +19,7 @@ public override async Task<int> ExecuteAsync(CommandContext context, DeleteSetti
1719
}
1820
else
1921
{
20-
console.Write(response.Value.Deleted.ToString().ToLowerInvariant());
22+
console.WriteLine(response.Value.Deleted.ToString().ToLowerInvariant());
2123
return 0;
2224
}
2325
}

src/dotnet-openai/File/ListCommand.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.ClientModel;
22
using System.ComponentModel;
33
using Humanizer;
4+
using Microsoft.Extensions.DependencyInjection;
45
using OpenAI;
56
using OpenAI.Files;
67
using Spectre.Console;
@@ -10,6 +11,7 @@
1011
namespace Devlooped.OpenAI.File;
1112

1213
[Description("List files")]
14+
[Service]
1315
class ListCommand(OpenAIClient oai, IAnsiConsole console, CancellationTokenSource cts) : AsyncCommand<ListCommand.ListSettings>
1416
{
1517
public override async Task<int> ExecuteAsync(CommandContext context, ListSettings settings)

src/dotnet-openai/File/UploadCommand.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.ComponentModel;
22
using System.ComponentModel.DataAnnotations;
3+
using Microsoft.Extensions.DependencyInjection;
34
using OpenAI;
45
using OpenAI.Files;
56
using Spectre.Console;
@@ -8,6 +9,7 @@
89
namespace Devlooped.OpenAI.File;
910

1011
[Description("Upload a local file, specifying its purpose.")]
12+
[Service]
1113
class UploadCommand(OpenAIClient oai, IAnsiConsole console, CancellationTokenSource cts) : AsyncCommand<UploadCommand.UploadSettings>
1214
{
1315
public override async Task<int> ExecuteAsync(CommandContext context, UploadSettings settings)

src/dotnet-openai/File/ViewCommand.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
using System.ComponentModel;
22
using Humanizer;
3+
using Microsoft.Extensions.DependencyInjection;
34
using OpenAI;
45
using Spectre.Console;
56
using Spectre.Console.Cli;
67

78
namespace Devlooped.OpenAI.File;
89

910
[Description("View a file by its ID.")]
11+
[Service]
1012
class ViewCommand(OpenAIClient oai, IAnsiConsole console, CancellationTokenSource cts) : AsyncCommand<ViewCommand.ViewSettings>
1113
{
1214
public override async Task<int> ExecuteAsync(CommandContext context, ViewSettings settings)

0 commit comments

Comments
 (0)