implement the first version.
This commit is contained in:
parent
f04d2a9ecf
commit
bed398fb39
22
llm-git-message.sln
Normal file
22
llm-git-message.sln
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.12.35728.132
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "lgm", "llm-git-message\lgm.csproj", "{E93E28C3-D88F-4228-94A5-43B3CAECC57E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{E93E28C3-D88F-4228-94A5-43B3CAECC57E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E93E28C3-D88F-4228-94A5-43B3CAECC57E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E93E28C3-D88F-4228-94A5-43B3CAECC57E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E93E28C3-D88F-4228-94A5-43B3CAECC57E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
176
llm-git-message/Program.cs
Normal file
176
llm-git-message/Program.cs
Normal file
@ -0,0 +1,176 @@
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace Blahblaho.LLM.GitMessage;
|
||||
|
||||
class Program
|
||||
{
|
||||
|
||||
static readonly JsonSerializerOptions defaultOptions = new(JsonSerializerDefaults.Web);
|
||||
|
||||
static int Main(string[] args)
|
||||
{
|
||||
string configFile = Path.Combine(Path.GetDirectoryName(Environment.CommandLine)!, "lgm.config.json");
|
||||
if (!File.Exists(configFile))
|
||||
{
|
||||
Console.WriteLine("Cannot find configuration file.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
Configure? configure;
|
||||
try
|
||||
{
|
||||
configure = JsonSerializer.Deserialize<Configure>(File.ReadAllText(configFile), defaultOptions);
|
||||
if (configure is null)
|
||||
{
|
||||
throw new FormatException("Failed to deserialize.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Invalid configuration file: {ex.Message}");
|
||||
return 1;
|
||||
}
|
||||
|
||||
var pi = new ProcessStartInfo
|
||||
{
|
||||
CreateNoWindow = false,
|
||||
FileName = "git",
|
||||
Arguments = "diff",
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
WorkingDirectory = Directory.GetCurrentDirectory(),
|
||||
RedirectStandardError = true,
|
||||
RedirectStandardOutput = true
|
||||
};
|
||||
var process = Process.Start(pi);
|
||||
|
||||
var sb = new StringBuilder();
|
||||
string? line;
|
||||
while ((line = process?.StandardOutput.ReadLine()) is not null)
|
||||
{
|
||||
sb.AppendLine(line);
|
||||
}
|
||||
|
||||
var request = J(
|
||||
("model", configure.Model),
|
||||
("messages", A(
|
||||
J(
|
||||
("role", "user"),
|
||||
("content", """
|
||||
You are a git commit expert. Based on the provided git diff result, generate a commit message that adheres to the following structure and explanations:
|
||||
|
||||
type prefix (feat, fix, etc.): The description is a concise summary of the change, Match the response language to the dominant language of code comments
|
||||
The optional body provides additional context or details about the change
|
||||
The optional footer provides breaking changes or issue references. e.g., Closes #123
|
||||
|
||||
Requirements:
|
||||
Merge similar modify and streamline the commit messages to no more than 5.
|
||||
""")),
|
||||
J(
|
||||
("role", "user"),
|
||||
("content", sb.ToString())))),
|
||||
("temperature", 0.5),
|
||||
//("top_p", 0.7),
|
||||
//("top_k", 50),
|
||||
("max_tokens", 4096),
|
||||
("stream", true)
|
||||
);
|
||||
|
||||
using var client = new HttpClient
|
||||
{
|
||||
Timeout = TimeSpan.FromMinutes(2),
|
||||
DefaultRequestVersion = HttpVersion.Version20,
|
||||
DefaultRequestHeaders =
|
||||
{
|
||||
Authorization = new AuthenticationHeaderValue("Bearer", configure.ApiKey)
|
||||
}
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
var sw = new Stopwatch();
|
||||
sw.Restart();
|
||||
|
||||
RequestText(client, configure.BaseUrl, request).Wait();
|
||||
Console.WriteLine($"\n\ncosts: {sw.Elapsed.TotalSeconds:n1} second(s)");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error occurs: {ex}");
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static async Task RequestText(HttpClient client, string baseUrl, JsonObject request)
|
||||
{
|
||||
using var requestMessage = new HttpRequestMessage(HttpMethod.Post, $"{baseUrl}chat/completions")
|
||||
{
|
||||
Content = new StringContent(JsonSerializer.Serialize(request), Encoding.UTF8, "application/json")
|
||||
};
|
||||
|
||||
using var response = await client.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead);
|
||||
using var stream = await response.Content.ReadAsStreamAsync();
|
||||
using var reader = new StreamReader(stream);
|
||||
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
var content = await reader.ReadLineAsync();
|
||||
if (!string.IsNullOrEmpty(content))
|
||||
{
|
||||
if (content.StartsWith("data: "))
|
||||
{
|
||||
content = content[6..];
|
||||
}
|
||||
if (content == "[DONE]")
|
||||
{
|
||||
break;
|
||||
}
|
||||
try
|
||||
{
|
||||
var data = JsonSerializer.Deserialize<JsonNode>(content, defaultOptions);
|
||||
if (data?["choices"]?[0]?["delta"] is JsonNode message)
|
||||
{
|
||||
string? text = null;
|
||||
string? think = null;
|
||||
|
||||
if (message["content"] is JsonNode cnt)
|
||||
{
|
||||
text = cnt.GetValue<string>();
|
||||
|
||||
Console.Write(text);
|
||||
}
|
||||
|
||||
if (message["reasoning_content"] is JsonNode tnk)
|
||||
{
|
||||
think = tnk.GetValue<string>();
|
||||
|
||||
Console.Write(think);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Deserialize (\"{content}\") error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static JsonObject J(params (string key, JsonNode? value)[] pairs)
|
||||
{
|
||||
return new JsonObject(pairs.Select(p => new KeyValuePair<string, JsonNode?>(p.key, p.value)));
|
||||
}
|
||||
|
||||
static JsonArray A(params JsonNode?[] array)
|
||||
{
|
||||
return new JsonArray(array);
|
||||
}
|
||||
}
|
||||
|
||||
record Configure(string BaseUrl, string Model, string ApiKey);
|
5
llm-git-message/lgm.config.json
Normal file
5
llm-git-message/lgm.config.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"baseUrl": "https://api.siliconflow.cn/v1/",
|
||||
"model": "Qwen/Qwen2.5-Coder-32B-Instruct",
|
||||
"apiKey": "your_api_key"
|
||||
}
|
19
llm-git-message/lgm.csproj
Normal file
19
llm-git-message/lgm.csproj
Normal file
@ -0,0 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<RootNamespace>Blahblaho.LLM.GitMessage</RootNamespace>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<!--<PublishAot>true</PublishAot>-->
|
||||
<InvariantGlobalization>true</InvariantGlobalization>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="lgm.config.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
Loading…
x
Reference in New Issue
Block a user