Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 21 additions & 14 deletions Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ static async Task<int> Main(string[] args)
"Temperature setting for AI model (0-2)"
);
var topPOption = new Option<int>("--topp", () => 1, "Top-p setting for AI model (0-1)");
var presenceOption = new Option<int>("--presence", () => 0, "Presence penalty for AI model");
var presenceOption = new Option<int>(
"--presence",
() => 0,
"Presence penalty for AI model"
);
var frequencyOption = new Option<int>(
"--frequency",
() => 0,
Expand All @@ -31,12 +35,11 @@ static async Task<int> Main(string[] args)
description: "AI model to use (overrides setup)"
);

var setupOption = new Option<bool>(
"--setup",
"Configure OpenAI or Azure OpenAI settings"
);
var setupOption = new Option<bool>("--setup", "Configure OpenAI or Azure OpenAI settings");

var rootCommand = new RootCommand("Generate AI-powered commit messages using OpenAI or Azure OpenAI")
var rootCommand = new RootCommand(
"Generate AI-powered commit messages using OpenAI or Azure OpenAI"
)
{
dryRunOption,
verboseOption,
Expand Down Expand Up @@ -120,8 +123,8 @@ static async Task GenerateCommitMessage(
if (string.IsNullOrEmpty(apiKey))
{
throw new InvalidOperationException(
"OpenAI API key not found. Please run 'WriteCommit --setup' to configure your API key " +
"or set the OPENAI_API_KEY environment variable."
"OpenAI API key not found. Please run 'WriteCommit --setup' to configure your API key "
+ "or set the OPENAI_API_KEY environment variable."
);
}

Expand All @@ -144,11 +147,13 @@ static async Task GenerateCommitMessage(
var stagedChanges = await gitService.GetStagedChangesAsync(verbose);

// If the diff is very small, grab a few extra lines of context
var fileCount = System.Text.RegularExpressions.Regex.Matches(
stagedChanges,
"^diff --git",
System.Text.RegularExpressions.RegexOptions.Multiline
).Count;
var fileCount = System
.Text.RegularExpressions.Regex.Matches(
stagedChanges,
"^diff --git",
System.Text.RegularExpressions.RegexOptions.Multiline
)
.Count;
var lineCount = stagedChanges.Split('\n').Length;

if (
Expand Down Expand Up @@ -250,7 +255,9 @@ static async Task RunSetupAsync(bool verbose)
{
Console.WriteLine();
Console.WriteLine("❌ Setup failed.");
Console.WriteLine("You can try again or set the OPENAI_API_KEY environment variable manually.");
Console.WriteLine(
"You can try again or set the OPENAI_API_KEY environment variable manually."
);
}
}
}
21 changes: 16 additions & 5 deletions Services/ConfigurationService.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.ClientModel;
using System.Text.Json;
using OpenAI.Chat;
using Azure.AI.OpenAI;
using System.ClientModel;
using OpenAI.Chat;
using WriteCommit.Constants;
using WriteCommit.Models;

Expand Down Expand Up @@ -114,7 +114,9 @@
Console.WriteLine("=================");
Console.WriteLine();
Console.WriteLine("Please enter your OpenAI API key (or Azure OpenAI key).");
Console.WriteLine("You can get one from: https://platform.openai.com/api-keys or your Azure portal");
Console.WriteLine(
"You can get one from: https://platform.openai.com/api-keys or your Azure portal"
);
Console.WriteLine();
Console.Write("API Key (leave blank if not required): ");

Expand Down Expand Up @@ -196,7 +198,13 @@
/// <summary>
/// Tests if the API key is valid by making a simple request
/// </summary>
private async Task<bool> TestApiKeyAsync(string? apiKey, bool useAzure, string endpoint, string model, bool verbose)
private async Task<bool> TestApiKeyAsync(
string? apiKey,
bool useAzure,
string endpoint,
string model,
bool verbose
)
{
Console.WriteLine("Testing API key...");

Expand All @@ -205,13 +213,16 @@
ChatClient testClient;
if (useAzure)
{
var azureClient = new Azure.AI.OpenAI.AzureOpenAIClient(new Uri(endpoint), new ApiKeyCredential(apiKey));
var azureClient = new Azure.AI.OpenAI.AzureOpenAIClient(
new Uri(endpoint),
new ApiKeyCredential(apiKey)

Check warning on line 218 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Test WriteCommit

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 218 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Test WriteCommit

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 218 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Build WriteCommit (ubuntu-latest)

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 218 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Build WriteCommit (ubuntu-latest)

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 218 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Build WriteCommit (windows-latest)

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 218 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Build WriteCommit (windows-latest)

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 218 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Build WriteCommit (macos-latest)

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 218 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Build WriteCommit (macos-latest)

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.
);
testClient = azureClient.GetChatClient(model);
}
else
{
var options = new OpenAI.OpenAIClientOptions { Endpoint = new Uri(endpoint) };
testClient = new ChatClient(model, new ApiKeyCredential(apiKey), options);

Check warning on line 225 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Test WriteCommit

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 225 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Test WriteCommit

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 225 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Build WriteCommit (ubuntu-latest)

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 225 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Build WriteCommit (ubuntu-latest)

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 225 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Build WriteCommit (windows-latest)

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 225 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Build WriteCommit (windows-latest)

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 225 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Build WriteCommit (macos-latest)

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.

Check warning on line 225 in Services/ConfigurationService.cs

View workflow job for this annotation

GitHub Actions / Build WriteCommit (macos-latest)

Possible null reference argument for parameter 'key' in 'ApiKeyCredential.ApiKeyCredential(string key)'.
}
var messages = new List<ChatMessage>
{
Expand Down
9 changes: 7 additions & 2 deletions Services/GitService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,18 @@ public async Task<string> GetStagedChangesAsync(bool verbose = false)
return result.Output;
}

public async Task<string> GetStagedChangesWithContextAsync(int contextLines, bool verbose = false)
public async Task<string> GetStagedChangesWithContextAsync(
int contextLines,
bool verbose = false
)
{
var args = $"--no-pager diff --staged --unified={contextLines}";
var result = await RunCommandAsync("git", args, verbose);
if (result.ExitCode != 0)
{
throw new InvalidOperationException($"Failed to get staged changes with context: {result.Error}");
throw new InvalidOperationException(
$"Failed to get staged changes with context: {result.Error}"
);
}
return result.Output;
}
Expand Down
49 changes: 39 additions & 10 deletions Services/OpenAIService.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Text;
using OpenAI.Chat;
using System.ClientModel;
using System.Text;
using Azure.AI.OpenAI;
using OpenAI.Chat;
using WriteCommit.Constants;
using WriteCommit.Models;

Expand All @@ -23,9 +23,7 @@ public OpenAIService(string apiKey, string? endpoint = null, bool useAzure = fal
}

_apiKey = apiKey;
_endpoint = string.IsNullOrWhiteSpace(endpoint)
? "https://api.openai.com/v1"
: endpoint;
_endpoint = string.IsNullOrWhiteSpace(endpoint) ? "https://api.openai.com/v1" : endpoint;
_useAzure = useAzure;
_patternsDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "patterns");
}
Expand All @@ -34,7 +32,10 @@ private ChatClient CreateChatClient(string model)
{
if (_useAzure)
{
var azureClient = new Azure.AI.OpenAI.AzureOpenAIClient(new Uri(_endpoint), new ApiKeyCredential(_apiKey));
var azureClient = new Azure.AI.OpenAI.AzureOpenAIClient(
new Uri(_endpoint),
new ApiKeyCredential(_apiKey)
);
return azureClient.GetChatClient(model);
}

Expand Down Expand Up @@ -249,7 +250,8 @@ bool verbose
}

var combinedContent = string.Join("\n\n", chunkMessages);
var estimatedTokens = EstimateTokenCount(systemPrompt) + EstimateTokenCount(combinedContent);
var estimatedTokens =
EstimateTokenCount(systemPrompt) + EstimateTokenCount(combinedContent);

if (estimatedTokens > MaxContextTokens && chunkMessages.Count > 1)
{
Expand All @@ -267,7 +269,16 @@ bool verbose
var msgTokens = EstimateTokenCount(msg);
if (currentTokens + msgTokens > MaxContextTokens / 2 && currentGroup.Count > 0)
{
var summary = await CombineChunkMessagesAsync(currentGroup, pattern, temperature, topP, presence, frequency, model, verbose);
var summary = await CombineChunkMessagesAsync(
currentGroup,
pattern,
temperature,
topP,
presence,
frequency,
model,
verbose
);
groupedSummaries.Add(summary);
currentGroup.Clear();
currentTokens = EstimateTokenCount(systemPrompt);
Expand All @@ -279,11 +290,29 @@ bool verbose

if (currentGroup.Count > 0)
{
var summary = await CombineChunkMessagesAsync(currentGroup, pattern, temperature, topP, presence, frequency, model, verbose);
var summary = await CombineChunkMessagesAsync(
currentGroup,
pattern,
temperature,
topP,
presence,
frequency,
model,
verbose
);
groupedSummaries.Add(summary);
}

return await CombineChunkMessagesAsync(groupedSummaries, pattern, temperature, topP, presence, frequency, model, verbose);
return await CombineChunkMessagesAsync(
groupedSummaries,
pattern,
temperature,
topP,
presence,
frequency,
model,
verbose
);
}

// Create a client for this specific model
Expand Down
Loading