From 8272d5d2146151accc6b2132a756119f818e55d8 Mon Sep 17 00:00:00 2001 From: Odin Luo Date: Thu, 21 Dec 2023 01:29:40 +0800 Subject: [PATCH] init --- ConfigDto.cs | 48 +++++++ ConfigHelper.cs | 49 ++++++++ FileHelper.cs | 56 +++++++++ MarkdownHelper.cs | 88 +++++++++++++ NetworkHelper.cs | 150 ++++++++++++++++++++++ OpenAIHelper.cs | 138 ++++++++++++++++++++ Program.cs | 152 +++++++++++++++++++++++ ReadMe.md | 24 ++++ TwoAPI.Sample.AnnotateMaster.csproj | 30 +++++ TwoAPI.Sample.AnnotateMaster.csproj.user | 6 + appsettings.json | 32 +++++ favicon.ico | Bin 0 -> 4286 bytes 12 files changed, 773 insertions(+) create mode 100644 ConfigDto.cs create mode 100644 ConfigHelper.cs create mode 100644 FileHelper.cs create mode 100644 MarkdownHelper.cs create mode 100644 NetworkHelper.cs create mode 100644 OpenAIHelper.cs create mode 100644 Program.cs create mode 100644 ReadMe.md create mode 100644 TwoAPI.Sample.AnnotateMaster.csproj create mode 100644 TwoAPI.Sample.AnnotateMaster.csproj.user create mode 100644 appsettings.json create mode 100644 favicon.ico diff --git a/ConfigDto.cs b/ConfigDto.cs new file mode 100644 index 0000000..27d71d9 --- /dev/null +++ b/ConfigDto.cs @@ -0,0 +1,48 @@ +// 2023-12-20 下午 11:42:10 +// 修改并优化ConfigDto.cs文件中的注释,用于SDK文档的导出 +namespace TwoAPI.Sample.AnnotateMaster +{ + /// + /// ConfigDto类用于封装和管理API配置信息。 + /// 此类提供了对API配置参数的访问,例如AI主机地址、AI密钥、模型信息等。 + /// + internal class ConfigDto + { + + /// + /// 获取或设置Steam服务的启用状态。 + /// 当值为true时,表示启用Steam服务;反之则为禁用。 + /// 此属性通常用于决定是否与Steam服务集成。 + /// + public bool? Steam { get; set; } + + /// + /// 获取或设置AI服务的主机地址。 + /// 此属性存储用于连接AI服务的网络地址。 + /// + public string? AIHost { get; set; } + + /// + /// 获取或设置用于访问AI服务的密钥。 + /// 此属性存储用于身份验证的API密钥,以确保安全访问。 + /// + public string? AIKey { get; set; } + + /// + /// 获取或设置当前使用的AI模型的标识。 + /// 此属性指明了AI服务中所使用的具体模型。 + /// + public string? Model { get; set; } + + /// + /// 获取或设置用于AI服务的默认提示信息。 + /// 此属性存储在请求AI服务时使用的初始提示文本。 + /// + public string? Prompt { get; set; } + + /// + /// 获取或设置支持的文件类型列表。 + /// + public string[]? SupportFiles { get; set; } + } +} \ No newline at end of file diff --git a/ConfigHelper.cs b/ConfigHelper.cs new file mode 100644 index 0000000..3b3b50a --- /dev/null +++ b/ConfigHelper.cs @@ -0,0 +1,49 @@ +using Newtonsoft.Json; + +namespace TwoAPI.Sample.AnnotateMaster +{ + /// + /// ConfigHelper类用于操作和获取应用程序的配置信息。 + /// + internal class ConfigHelper + { + /// + /// 获取应用程序配置信息的静态方法。 + /// + /// 返回ConfigDto类型的配置信息对象。如果读取失败,则返回一个新实例。 + internal static ConfigDto GetConfig() + { + try + { + // 获取当前应用程序的目录路径。 + string currentDirectory = Directory.GetCurrentDirectory(); + + // 构造配置文件的完整路径。 + string configFilePath = Path.Combine(currentDirectory, "appsettings.json"); + + // 检查配置文件是否存在,不存在则抛出异常。 + if (!File.Exists(configFilePath)) + { + throw new FileNotFoundException("Configuration file not found."); + } + + // 读取配置文件的内容为字符串格式。 + string json = File.ReadAllText(configFilePath); + + // 将JSON字符串反序列化为ConfigDto对象。 + var config = JsonConvert.DeserializeObject(json); + // 如果解析结果为null,则创建一个新的ConfigDto实例。 + if (config == null) + return new ConfigDto(); + + // 返回解析成功的ConfigDto对象。 + return config; + } + catch (Exception) + { + // 在发生任何异常的情况下,返回一个新的ConfigDto实例。 + return new ConfigDto(); + } + } + } +} diff --git a/FileHelper.cs b/FileHelper.cs new file mode 100644 index 0000000..7e5fe0b --- /dev/null +++ b/FileHelper.cs @@ -0,0 +1,56 @@ +namespace TwoAPI.Sample.AnnotateMaster +{ + internal class FileHelper + { + /// + /// 获取唯一的输出路径。若指定的输出路径已存在,则通过在路径后附加特定后缀(例如 "_bak1")来生成唯一路径。 + /// + /// 原始输出路径 + /// 如果原始路径已存在,返回修改后的唯一路径;否则返回原始路径 + internal static string GetUniqueOutputPath(string outputPath) + { + int count = 1; + string uniqueOutputPath = outputPath; + + // 如果输出路径已存在,则在路径后添加"_bak{count}"来生成唯一的输出路径 + while (Directory.Exists(uniqueOutputPath)) + { + uniqueOutputPath = $"{outputPath}_bak{count}"; + count++; + } + + return uniqueOutputPath; + } + + /// + /// 判断指定文件是否为文本文件。此方法基于文件扩展名进行判断。 + /// + /// 文件路径 + /// 如果文件是支持的文本文件类型,则返回true;否则返回false + internal static bool IsTextFile(string filePath) + { + var config = ConfigHelper.GetConfig(); + + // 获取文件扩展名,并转换为小写字母形式 + var extension = Path.GetExtension(filePath).ToLower(); + + // 获取支持的文本文件扩展名列表 + var textExtensions = config.SupportFiles ?? [".cs"]; + + // 判断文件扩展名是否在支持的文本文件扩展名列表中,如果是则返回true,否则返回false + return textExtensions.Contains(extension); + } + + /// + /// 判断指定文件是否为小型文件。通过比较文件大小与指定的最大大小来决定。 + /// + /// 文件路径 + /// 定义小型文件的最大大小(单位:字节) + /// 如果文件大小小于指定的最大大小,则返回true;否则返回false + internal static bool IsSmallFile(string filePath, int maxSize) + { + FileInfo fileInfo = new(filePath); + return fileInfo.Length < maxSize; + } + } +} diff --git a/MarkdownHelper.cs b/MarkdownHelper.cs new file mode 100644 index 0000000..06c6424 --- /dev/null +++ b/MarkdownHelper.cs @@ -0,0 +1,88 @@ +using System.Text.RegularExpressions; + +namespace TwoAPI.Sample.AnnotateMaster +{ + /// + /// MarkdownHelper类是一个帮助处理Markdown格式代码的类,它主要用于提取Markdown格式内容中的代码块。 + /// + internal partial class MarkdownHelper + { + + /// + /// ExtractCode方法用于从Markdown格式的内容中提取代码块,并返回提取到的代码字符串。 + /// 如果传入的内容为空或者不符合Markdown格式,该方法将直接返回原始内容。 + /// + /// + /// + internal static string ExtractCode(string? content) + { + if (string.IsNullOrWhiteSpace(content)) + { + return ""; + } + + // 检查内容是否符合Markdown格式,如果是则进行提取操作,否则直接返回原始输入。 + if (IsMarkdownFormat(content)) + { + // 从Markdown内容中提取第一个代码块并返回 + return ExtractCodeFromMarkdown(content); + } + else + { + // 返回原始内容(不包含任何修改) + return content; + } + } + + /// + /// IsMarkdownFormat方法用于判断给定的内容是否符合Markdown格式。 + /// 判断的标准是内容是否以三个反引号和语言指示符开头。 + /// + /// + /// + internal static bool IsMarkdownFormat(string content) + { + // 使用正则表达式检查内容是否以三个反引号开头,并且后面跟着语言指示器(即判断是否是代码块) + return IsMarkdownRegex().IsMatch(content); + } + + /// + /// ExtractCodeFromMarkdown方法用于从给定的Markdown内容中提取第一个代码块。 + /// 该方法会尝试找到Markdown内容中的第一个代码块,如果找到,就返回该代码块的内容;如果没有找到,就返回空字符串。 + /// + /// + /// + internal static string ExtractCodeFromMarkdown(string content) + { + // 在Markdown内容中查找第一个代码块 + Match match = GetCodeRegex().Match(content); + + if (match.Success) + { + // 从匹配组中提取出代码 + return match.Groups[1].Value; + } + else + { + // 没有找到任何代码块, 返回空字符串或者根据需要处理错误 + return string.Empty; + } + } + + /// + /// GetCodeRegex方法是获取一个匹配Markdown格式代码块的正则表达式对象的方法。 + /// 该方法返回一个Regex对象,该对象用于匹配以三个反引号开头,且后面跟着某种语言指示器的代码块。 + /// + /// + [GeneratedRegex(@"```.*\n([\s\S]*?)\n```")] + internal static partial Regex GetCodeRegex(); + + /// + /// IsMarkdownRegex方法是获取一个判断是否为Markdown格式的正则表达式对象的方法。 + /// 该方法返回一个Regex对象,该对象用于判断内容是否以三个反引号和语言指示器开头。 + /// + /// + [GeneratedRegex(@"^```[\w\s]*\n")] + internal static partial Regex IsMarkdownRegex(); + } +} diff --git a/NetworkHelper.cs b/NetworkHelper.cs new file mode 100644 index 0000000..26304cc --- /dev/null +++ b/NetworkHelper.cs @@ -0,0 +1,150 @@ +using Newtonsoft.Json; +using System.Net; +using System.Net.Http.Headers; +using TwoAPI.DataContracts.Consts; +using TwoAPI.DataContracts.Response.Vendors.OpenAI; + +namespace TwoAPI.Sample.AnnotateMaster +{ + /// + /// 定义NetworkHelper内部局部类,辅助网络请求操作 + /// + internal partial class NetworkHelper + { + private const int DefaultTimeoutMs = 600_000; // 默认超时时间为600秒 + /// + /// 异步发送POST请求到AI服务。 + /// + /// 需要发送的代码。 + /// 文件名,用于辅助AI服务理解上下文。 + /// 请求超时时间,以毫秒为单位,默认为600000毫秒。 + /// 取消操作的令牌。 + /// AI服务的响应字符串。 + internal static async Task PostAsync(string code, string fileName, int? timeoutMs = DefaultTimeoutMs, CancellationToken cancellationToken = default) + { + var config = ConfigHelper.GetConfig(); // 获取配置信息对象 + string baseUrl = config.AIHost ?? ""; // 获取AI主机地址配置项值 + string apiKey = config.AIKey ?? ""; // 获取AI密钥配置项值 + + var question = OpenAIHelper.GetPrompt(code, fileName); + var requestModel = OpenAIHelper.GetCompletionsRequest(question); + + var httpClientHandler = new HttpClientHandler + { + ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true, + AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate | DecompressionMethods.None + /* + 设置HttpClientHandler对象的属性: + - ServerCertificateCustomValidationCallback:设置服务器证书验证回调函数,始终返回true表示信任所有服务器证书; + - AutomaticDecompression:设置自动解压缩方法,支持GZip、Deflate和None三种解压缩方式。 + */ + }; + + if (string.IsNullOrWhiteSpace(baseUrl)) + { + baseUrl = OpenAIV1.Host; + } + else + { + var uri = new Uri(baseUrl); + baseUrl = uri.GetLeftPart(UriPartial.Authority); + } + + var _httpClient = new HttpClient(httpClientHandler) + { + Timeout = TimeSpan.FromMilliseconds(timeoutMs ?? DefaultTimeoutMs), + BaseAddress = new Uri(baseUrl) + }; + ServicePointManager.DefaultConnectionLimit = int.MaxValue; + + if (!string.IsNullOrEmpty(apiKey)) + { + _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}"); + } + else + { + try + { + _httpClient.DefaultRequestHeaders.Remove("Authorization"); + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + } + } + + var path = OpenAIHelper.GetChatCompletionsPath(baseUrl); + var settings = new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore + }; + var json = JsonConvert.SerializeObject(requestModel, settings); + var content = new StringContent(json); + using var request = CreatePostJsonRequest(path, content); + var response = await SendRequestAsync(_httpClient, request, cancellationToken); + var stream = await response.Content.ReadAsStreamAsync(cancellationToken); + var reader = new StreamReader(stream); + if (!(config.Steam ?? false)) + { + var lines = await reader.ReadToEndAsync(cancellationToken); + var obj = JsonConvert.DeserializeObject(lines); + return MarkdownHelper.ExtractCode(obj?.Choices?[0].Message?.Content); + } + else + { + var contents = string.Empty; + while (!reader.EndOfStream) + { + cancellationToken.ThrowIfCancellationRequested(); + + var line = await reader.ReadLineAsync(cancellationToken); + if (line == "data: " || line == null) + { + continue; + } + + if (line.StartsWith("[DONE]") || line.Equals("data: [DONE]")) + { + break; + } + + contents = OpenAIHelper.GetContentByLine(contents, line); + } + + return contents; + } + } + + /// + /// 创建一个以JSON格式发送的POST请求。 + /// + /// 请求的URI。 + /// 请求的内容。 + /// 配置好的HttpRequestMessage实例。 + private static HttpRequestMessage CreatePostJsonRequest(string uri, HttpContent content) + { + var request = new HttpRequestMessage(HttpMethod.Post, uri); + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + content.Headers.ContentType = new MediaTypeHeaderValue("application/json") + { + CharSet = "utf-8" + }; + + request.Content = content; + return request; + } + + /// + /// 使用指定的HttpClient异步发送请求。 + /// + /// 用于发送请求的HttpClient实例。 + /// 需要发送的HttpRequestMessage实例。 + /// 取消操作的令牌。 + /// 服务器响应的HttpResponseMessage实例。 + private static async Task SendRequestAsync(HttpClient _httpClient, HttpRequestMessage request, CancellationToken cancellationToken) + { + var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken); + return response; + } + } +} diff --git a/OpenAIHelper.cs b/OpenAIHelper.cs new file mode 100644 index 0000000..e75486d --- /dev/null +++ b/OpenAIHelper.cs @@ -0,0 +1,138 @@ +using Newtonsoft.Json; +using TwoAPI.DataContracts.Consts; // 引用常量定义 +using TwoAPI.DataContracts.Node.Vendor.OpenAI; // 引用OpenAI节点数据契约 +using TwoAPI.DataContracts.Request.Vendors.OpenAI; +using TwoAPI.DataContracts.Response.Vendors.OpenAI; // 引用OpenAI请求数据契约 + +namespace TwoAPI.Sample.AnnotateMaster +{ + /// + /// OpenAIHelper类提供与OpenAI服务交互的辅助方法。 + /// + internal class OpenAIHelper + { + /// + /// GetResponse方法用于将JSON格式的字符串解析为CompletionCreateResponse对象。 + /// 若输入字符串为空或为"[DONE]",则返回null。 + /// + /// 代表JSON格式的响应字符串。 + /// 解析后的CompletionCreateResponse对象,解析失败时返回空对象。 + internal static CompletionCreateResponse? GetResponse(string line) + { + if (string.IsNullOrEmpty(line) || line.Equals("[DONE]")) return null; + + try + { + return JsonConvert.DeserializeObject(line); + } + catch (Exception) + { + return new CompletionCreateResponse(); + } + } + + /// + /// GetContentByLine方法用于从OpenAI的响应中提取并累加内容字符串。 + /// + /// 之前累加的内容字符串。 + /// 当前处理的响应字符串。 + /// 更新后的内容字符串。 + internal static string GetContentByLine(string contents, string line) + { + try + { + if (line.StartsWith("data: ")) + { + line = line["data: ".Length..]; + } + + //line = line.Trim(); + if (string.IsNullOrWhiteSpace(line)) + return contents; + + var obj = GetResponse(line); + + var str = string.Empty; + if (obj != null && !string.IsNullOrWhiteSpace(obj?.Choices?[0].Delta?.Content)) + str = obj.Choices[0].Delta.Content; + + if (obj != null && !string.IsNullOrWhiteSpace(obj?.Choices?[0].Message?.Content)) + str = obj.Choices[0].Message.Content; + + Console.Write(str); + contents += str; + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + } + + return contents; + } + + /// + /// GetCompletionsRequest方法用于创建并返回一个CompletionsRequest对象, + ///该对象包含了向OpenAI发送请求所需的参数信息。 + /// + /// 需要向OpenAI提交的问题字符串。 + /// 配置好的CompletionsRequest对象。 + internal static CompletionsRequest GetCompletionsRequest(string question) + { + var config = ConfigHelper.GetConfig(); // 获取配置信息 + + var request = new CompletionsRequest() + { + FrequencyPenalty = 1, // 设置频率惩罚,以减少重复内容的生成概率 + MaxTokens = 8000, // 设置生成文本的最大令牌数 + Messages = + [ + new ChatCompletionMessageNode() // 初始化聊天消息节点 + { + Role = "user", // 指定发送者角色为用户 + Content = question // 设置消息内容为传入的问题 + } + ], + Model = config.Model, // 设置模型名称,使用配置文件中指定的模型 + Stream = config.Steam ?? false // 设置是否以流的形式接收响应,此处默认设为非流式 + }; + + return request; + } + + /// + /// GetPrompt方法用于获取一个提示字符串,该字符串用于在问题和文件名中插入到配置的提示模板中。 + /// + /// 问题字符串。 + /// 文件名。 + /// 构建的提示字符串。 + internal static string GetPrompt(string question, string fileName) + { + var tmpQuestion = GetNeedOnlinePrompt(question, fileName); // 获取在线处理所需的提示信息 + return tmpQuestion; // 返回构建的提示字符串 + } + + /// + /// GetNeedOnlinePrompt方法根据给定的问题和文件名,使用配置中的提示模板生成一个需要在线处理的提示字符串。 + /// + /// + /// + /// + internal static string GetNeedOnlinePrompt(string question, string fileName) + { + var config = ConfigHelper.GetConfig(); // 获取配置信息 + // 返回格式化后的提示字符串,包含文件名、问题内容和当前时间信息 + return string.Format(config.Prompt ?? "请对{0}的内容添加注释,内容如下{1},在内容顶部添加一个注释更新信息{2}", fileName, question, DateTime.Now.ToString()); + } + + /// + /// GetChatCompletionsPath方法根据给定的baseUrl,返回与聊天完成相关接口路径。 + /// + /// + /// + internal static string GetChatCompletionsPath(string baseUrl) + { + // 如果baseUrl为空或空白,则直接使用默认的ChatCompletions路径,否则构建完整路径 + return string.IsNullOrWhiteSpace(baseUrl) ? OpenAIV1.ChatCompletions : $"{new Uri(baseUrl).AbsolutePath.TrimStart('/')}/{OpenAIV1.ChatCompletions}".Trim('/'); + } + } +} diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..e255302 --- /dev/null +++ b/Program.cs @@ -0,0 +1,152 @@ +namespace TwoAPI.Sample.AnnotateMaster +{ + /// + /// 主程序类,负责处理文件夹内程序代码的注释添加和优化。 + /// + internal class Program + { + /// + /// 程序的主入口点。 + /// + /// 命令行参数。 + static void Main(string[] args) + { + string? folderPath = null; + string? outputPath = null; + + // 检查 f 参数,获取文件夹路径 + for (int i = 0; i < args.Length; i++) + { + if (args[i] == "f" && i + 1 < args.Length) + { + folderPath = args[i + 1]; + break; + } + } + + // 如果未指定 f 参数,则提示输入文件夹地址 + while (string.IsNullOrEmpty(folderPath)) + { + Console.WriteLine("请输入文件夹地址 (f 参数):"); + folderPath = Console.ReadLine(); + } + + // 检查文件夹是否存在 + // 如果不存在,则要求重新输入文件夹地址,直到输入正确为止。 + // 这里没有对路径进行验证,只是简单地检查了目录是否存在。 + // 可以根据实际需求添加更多的验证逻辑。 + while (!Directory.Exists(folderPath)) + { + Console.WriteLine("文件夹不存在,请重新输入文件夹地址:"); + folderPath = Console.ReadLine(); + } + + // 检查 o 参数 + for (int i = 0; i < args.Length; i++) + { + if (args[i] == "o" && i + 1 < args.Length) + { + outputPath = args[i + 1]; + break; + } + } + + // 如果未指定 o 参数,则在 f 地址后添加 "_bak" 作为默认输出地址 + // 这里使用了一个自定义的方法 GetUniqueOutputPath 来生成唯一的输出文件夹路径。 + // 可以根据实际需求修改该方法的实现。 + + if (string.IsNullOrEmpty(outputPath)) + { + outputPath = FileHelper.GetUniqueOutputPath(folderPath); + } + + try + { + // 创建输出文件夹 + Directory.CreateDirectory(outputPath); + // 遍历文件夹中的文件,并复制到输出文件夹 + TraverseFolder(folderPath, outputPath, folderPath); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + + Console.WriteLine("操作完成,请按任意键退出程序..."); + Console.ReadKey(); + } + + /// + /// 遍历指定文件夹,复制文件到输出路径,并进行文件内容处理。 + /// + /// 要遍历的文件夹路径。 + /// 文件处理输出路径。 + /// 原始文件夹路径,用于生成相对路径。 + static void TraverseFolder(string folderPath, string outputPath, string orginalPath) + { + foreach (string filePath in Directory.GetFiles(folderPath)) + { + var fileName = Path.GetFileName(filePath); + var subPath = filePath.TrimEnd(fileName.ToCharArray()).Replace(orginalPath, "").TrimStart("\\".ToCharArray()); + var outputFolderPath = Path.Combine(outputPath, subPath); + + // 创建输出文件夹 + Directory.CreateDirectory(outputFolderPath); + + var outputFilePath = Path.Combine(outputPath, subPath, fileName); + + + // 复制文件 + File.Copy(filePath, outputFilePath); + Console.WriteLine("复制文件: " + outputFilePath); + + // 检查文件类型和大小 + // 使用 FileHelper 类的 IsTextFile 和 IsSmallFile 方法来判断文件是否为文本文件且小于指定大小。 + + if (FileHelper.IsTextFile(filePath) && FileHelper.IsSmallFile(filePath, 7 * 1024)) + { + Console.WriteLine("开始AI优化文件: " + fileName); + + // 打开文本文件并输出文件名和内容 + var fileContent = File.ReadAllText(filePath); + + if (!string.IsNullOrWhiteSpace(fileContent)) + { + var newContent = string.Empty; + try + { + newContent = NetworkHelper.PostAsync(fileContent, fileName).GetAwaiter().GetResult(); + Console.WriteLine("优化后文件: " + outputFilePath); + } + catch (Exception ex) + { + Console.WriteLine("优化文件失败: " + outputFilePath); + Console.WriteLine(ex.Message); + } + + + if (!string.IsNullOrWhiteSpace(newContent)) + { + FileAttributes attributes = File.GetAttributes(outputFilePath); + + if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) + { + // 取消只读属性 + attributes &= ~FileAttributes.ReadOnly; + File.SetAttributes(outputFilePath, attributes); + } + + // 执行写入操作 + File.WriteAllText(outputFilePath, newContent); + } + } + } + } + + foreach (string subFolderPath in Directory.GetDirectories(folderPath)) + { + TraverseFolder(subFolderPath, outputPath, orginalPath); // 递归调用遍历子文件夹 + } + } + } +} diff --git a/ReadMe.md b/ReadMe.md new file mode 100644 index 0000000..34268ab --- /dev/null +++ b/ReadMe.md @@ -0,0 +1,24 @@ +# 一键给文件夹中所有的代码添加注释的应用 +![Build Status](https://img.shields.io/badge/build-passing-brightgreen) ![License](https://img.shields.io/badge/license-MIT-blue) ![.NET8](https://img.shields.io/badge/.NET-8-blueviolet) + +这是一个使用 .NET 8 开发的开源项目,旨在方便开发者为项目中的所有代码一键添加注释。本项目默认使用 GPT 进行注释的添加,操作简便,用户只需运行程序,然后输入需要添加注释的代码的文件夹路径即可。 + +## 特性 + +- **安全性**:本程序会在添加注释前,先将当前项目内容复制到一个新的 `_bak` 文件夹中,并在这个文件夹内完成添加注释的工作,保证了源代码的安全性。 + +- **灵活性**:本项目支持使用不同版本的 GPT 进行注释操作,推荐使用 `gpt-4-32k`。如果预算有限,也可以选择使用 `gpt-3.5-turbo-16k`。 + +## 如何使用 + +1. 克隆或下载本项目到本地 +2. 打开终端,运行程序 +3. 当提示输入文件夹路径时,输入需要添加注释的代码的文件夹路径 + +## 注意事项 + +由于 ChatGPT 的幻觉问题依然十分严重,所以我们建议大家使用 `gpt-4-32k` 来进行注释操作。如果预算有限,可以选择使用 `gpt-3.5-turbo-16k` 进行替代。 + +## 许可证 + +本项目采用 MIT 许可证,欢迎任何人对我们的项目进行改进并分享。 \ No newline at end of file diff --git a/TwoAPI.Sample.AnnotateMaster.csproj b/TwoAPI.Sample.AnnotateMaster.csproj new file mode 100644 index 0000000..9067ff1 --- /dev/null +++ b/TwoAPI.Sample.AnnotateMaster.csproj @@ -0,0 +1,30 @@ + + + + Exe + net8.0 + enable + enable + TwoAPI.Sample.AnnotateMaster.Program + favicon.ico + + + + + + + + + + + + + + + + + Always + + + + diff --git a/TwoAPI.Sample.AnnotateMaster.csproj.user b/TwoAPI.Sample.AnnotateMaster.csproj.user new file mode 100644 index 0000000..5b0b2c3 --- /dev/null +++ b/TwoAPI.Sample.AnnotateMaster.csproj.user @@ -0,0 +1,6 @@ + + + + <_LastSelectedProfileId>C:\Workspace\github\twoapi\twoapi-core\TwoAPI.Sample.AnnotateMaster\Properties\PublishProfiles\ClickOnceProfile.pubxml + + \ No newline at end of file diff --git a/appsettings.json b/appsettings.json new file mode 100644 index 0000000..0e99102 --- /dev/null +++ b/appsettings.json @@ -0,0 +1,32 @@ +{ + "AIHost": "https://twoapi.qiangtu.com", // AI服务的主机地址 + "AIKey": "sk-gVM7zxAQkuR5slSp57238cE56aBf45128cF3BdE4Ff782b24", // AI服务的密钥 + "Model": "gpt-3.5-turbo-16k", // 使用的模型 + "SupportFiles": [ // 支持的文件类型列表 + ".js", + ".cs", + ".java", + ".py", + ".cpp", + ".c", + ".h", + ".hpp", + ".php", + ".rb", + ".swift", + ".go", + ".lua", + ".pl", + ".sh", + ".sql", + ".asm", + ".vb", + ".asmx", + ".aspx", + ".jsp", + ".ts", + ".kt" + ], + "Steam": false, // 设置是否以流的形式接收响应,此处默认设为非流式 + "Prompt": "作为一位经验丰富的高级开发人员,请以导出SDK文档为目的,为代码文件{0}内的对象、方法和类添加、更正、优化注释。\n## 只返回修改过的{0}文件\n- 不更改代码逻辑和命名\n- 返回的代码将用于替换原始代码,请不要返回代码之外的内容\n- 使用简体中文\n- 不要使用Markdown标记包含代码内容\n- 在代码开始处添加一行注释{2}\n- 原样包含引用、注释和代码\n\n以下是需要被注释的代码文件{0}的内容:\n```\n{1}\n```" +} \ No newline at end of file diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e00f598e01ae73c5f9baba24f16b4cac189008cb GIT binary patch literal 4286 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