-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Odin Luo
authored and
Odin Luo
committed
Dec 20, 2023
0 parents
commit 8272d5d
Showing
12 changed files
with
773 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// 2023-12-20 下午 11:42:10 | ||
// 修改并优化ConfigDto.cs文件中的注释,用于SDK文档的导出 | ||
namespace TwoAPI.Sample.AnnotateMaster | ||
{ | ||
/// <summary> | ||
/// ConfigDto类用于封装和管理API配置信息。 | ||
/// 此类提供了对API配置参数的访问,例如AI主机地址、AI密钥、模型信息等。 | ||
/// </summary> | ||
internal class ConfigDto | ||
{ | ||
|
||
/// <summary> | ||
/// 获取或设置Steam服务的启用状态。 | ||
/// 当值为true时,表示启用Steam服务;反之则为禁用。 | ||
/// 此属性通常用于决定是否与Steam服务集成。 | ||
/// </summary> | ||
public bool? Steam { get; set; } | ||
|
||
/// <summary> | ||
/// 获取或设置AI服务的主机地址。 | ||
/// 此属性存储用于连接AI服务的网络地址。 | ||
/// </summary> | ||
public string? AIHost { get; set; } | ||
|
||
/// <summary> | ||
/// 获取或设置用于访问AI服务的密钥。 | ||
/// 此属性存储用于身份验证的API密钥,以确保安全访问。 | ||
/// </summary> | ||
public string? AIKey { get; set; } | ||
|
||
/// <summary> | ||
/// 获取或设置当前使用的AI模型的标识。 | ||
/// 此属性指明了AI服务中所使用的具体模型。 | ||
/// </summary> | ||
public string? Model { get; set; } | ||
|
||
/// <summary> | ||
/// 获取或设置用于AI服务的默认提示信息。 | ||
/// 此属性存储在请求AI服务时使用的初始提示文本。 | ||
/// </summary> | ||
public string? Prompt { get; set; } | ||
|
||
/// <summary> | ||
/// 获取或设置支持的文件类型列表。 | ||
/// </summary> | ||
public string[]? SupportFiles { get; set; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
using Newtonsoft.Json; | ||
|
||
namespace TwoAPI.Sample.AnnotateMaster | ||
{ | ||
/// <summary> | ||
/// ConfigHelper类用于操作和获取应用程序的配置信息。 | ||
/// </summary> | ||
internal class ConfigHelper | ||
{ | ||
/// <summary> | ||
/// 获取应用程序配置信息的静态方法。 | ||
/// </summary> | ||
/// <returns>返回ConfigDto类型的配置信息对象。如果读取失败,则返回一个新实例。</returns> | ||
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<ConfigDto>(json); | ||
// 如果解析结果为null,则创建一个新的ConfigDto实例。 | ||
if (config == null) | ||
return new ConfigDto(); | ||
|
||
// 返回解析成功的ConfigDto对象。 | ||
return config; | ||
} | ||
catch (Exception) | ||
{ | ||
// 在发生任何异常的情况下,返回一个新的ConfigDto实例。 | ||
return new ConfigDto(); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
namespace TwoAPI.Sample.AnnotateMaster | ||
{ | ||
internal class FileHelper | ||
{ | ||
/// <summary> | ||
/// 获取唯一的输出路径。若指定的输出路径已存在,则通过在路径后附加特定后缀(例如 "_bak1")来生成唯一路径。 | ||
/// </summary> | ||
/// <param name="outputPath">原始输出路径</param> | ||
/// <returns>如果原始路径已存在,返回修改后的唯一路径;否则返回原始路径</returns> | ||
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; | ||
} | ||
|
||
/// <summary> | ||
/// 判断指定文件是否为文本文件。此方法基于文件扩展名进行判断。 | ||
/// </summary> | ||
/// <param name="filePath">文件路径</param> | ||
/// <returns>如果文件是支持的文本文件类型,则返回true;否则返回false</returns> | ||
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); | ||
} | ||
|
||
/// <summary> | ||
/// 判断指定文件是否为小型文件。通过比较文件大小与指定的最大大小来决定。 | ||
/// </summary> | ||
/// <param name="filePath">文件路径</param> | ||
/// <param name="maxSize">定义小型文件的最大大小(单位:字节)</param> | ||
/// <returns>如果文件大小小于指定的最大大小,则返回true;否则返回false</returns> | ||
internal static bool IsSmallFile(string filePath, int maxSize) | ||
{ | ||
FileInfo fileInfo = new(filePath); | ||
return fileInfo.Length < maxSize; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
using System.Text.RegularExpressions; | ||
|
||
namespace TwoAPI.Sample.AnnotateMaster | ||
{ | ||
/// <summary> | ||
/// MarkdownHelper类是一个帮助处理Markdown格式代码的类,它主要用于提取Markdown格式内容中的代码块。 | ||
/// </summary> | ||
internal partial class MarkdownHelper | ||
{ | ||
|
||
/// <summary> | ||
/// ExtractCode方法用于从Markdown格式的内容中提取代码块,并返回提取到的代码字符串。 | ||
/// 如果传入的内容为空或者不符合Markdown格式,该方法将直接返回原始内容。 | ||
/// </summary> | ||
/// <param name="content"></param> | ||
/// <returns></returns> | ||
internal static string ExtractCode(string? content) | ||
{ | ||
if (string.IsNullOrWhiteSpace(content)) | ||
{ | ||
return ""; | ||
} | ||
|
||
// 检查内容是否符合Markdown格式,如果是则进行提取操作,否则直接返回原始输入。 | ||
if (IsMarkdownFormat(content)) | ||
{ | ||
// 从Markdown内容中提取第一个代码块并返回 | ||
return ExtractCodeFromMarkdown(content); | ||
} | ||
else | ||
{ | ||
// 返回原始内容(不包含任何修改) | ||
return content; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// IsMarkdownFormat方法用于判断给定的内容是否符合Markdown格式。 | ||
/// 判断的标准是内容是否以三个反引号和语言指示符开头。 | ||
/// </summary> | ||
/// <param name="content"></param> | ||
/// <returns></returns> | ||
internal static bool IsMarkdownFormat(string content) | ||
{ | ||
// 使用正则表达式检查内容是否以三个反引号开头,并且后面跟着语言指示器(即判断是否是代码块) | ||
return IsMarkdownRegex().IsMatch(content); | ||
} | ||
|
||
/// <summary> | ||
/// ExtractCodeFromMarkdown方法用于从给定的Markdown内容中提取第一个代码块。 | ||
/// 该方法会尝试找到Markdown内容中的第一个代码块,如果找到,就返回该代码块的内容;如果没有找到,就返回空字符串。 | ||
/// </summary> | ||
/// <param name="content"></param> | ||
/// <returns></returns> | ||
internal static string ExtractCodeFromMarkdown(string content) | ||
{ | ||
// 在Markdown内容中查找第一个代码块 | ||
Match match = GetCodeRegex().Match(content); | ||
|
||
if (match.Success) | ||
{ | ||
// 从匹配组中提取出代码 | ||
return match.Groups[1].Value; | ||
} | ||
else | ||
{ | ||
// 没有找到任何代码块, 返回空字符串或者根据需要处理错误 | ||
return string.Empty; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// GetCodeRegex方法是获取一个匹配Markdown格式代码块的正则表达式对象的方法。 | ||
/// 该方法返回一个Regex对象,该对象用于匹配以三个反引号开头,且后面跟着某种语言指示器的代码块。 | ||
/// </summary> | ||
/// <returns></returns> | ||
[GeneratedRegex(@"```.*\n([\s\S]*?)\n```")] | ||
internal static partial Regex GetCodeRegex(); | ||
|
||
/// <summary> | ||
/// IsMarkdownRegex方法是获取一个判断是否为Markdown格式的正则表达式对象的方法。 | ||
/// 该方法返回一个Regex对象,该对象用于判断内容是否以三个反引号和语言指示器开头。 | ||
/// </summary> | ||
/// <returns></returns> | ||
[GeneratedRegex(@"^```[\w\s]*\n")] | ||
internal static partial Regex IsMarkdownRegex(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
{ | ||
/// <summary> | ||
/// 定义NetworkHelper内部局部类,辅助网络请求操作 | ||
/// </summary> | ||
internal partial class NetworkHelper | ||
{ | ||
private const int DefaultTimeoutMs = 600_000; // 默认超时时间为600秒 | ||
/// <summary> | ||
/// 异步发送POST请求到AI服务。 | ||
/// </summary> | ||
/// <param name="code">需要发送的代码。</param> | ||
/// <param name="fileName">文件名,用于辅助AI服务理解上下文。</param> | ||
/// <param name="timeoutMs">请求超时时间,以毫秒为单位,默认为600000毫秒。</param> | ||
/// <param name="cancellationToken">取消操作的令牌。</param> | ||
/// <returns>AI服务的响应字符串。</returns> | ||
internal static async Task<string?> 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<CompletionCreateResponse>(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; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// 创建一个以JSON格式发送的POST请求。 | ||
/// </summary> | ||
/// <param name="uri">请求的URI。</param> | ||
/// <param name="content">请求的内容。</param> | ||
/// <returns>配置好的HttpRequestMessage实例。</returns> | ||
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; | ||
} | ||
|
||
/// <summary> | ||
/// 使用指定的HttpClient异步发送请求。 | ||
/// </summary> | ||
/// <param name="_httpClient">用于发送请求的HttpClient实例。</param> | ||
/// <param name="request">需要发送的HttpRequestMessage实例。</param> | ||
/// <param name="cancellationToken">取消操作的令牌。</param> | ||
/// <returns>服务器响应的HttpResponseMessage实例。</returns> | ||
private static async Task<HttpResponseMessage> SendRequestAsync(HttpClient _httpClient, HttpRequestMessage request, CancellationToken cancellationToken) | ||
{ | ||
var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken); | ||
return response; | ||
} | ||
} | ||
} |
Oops, something went wrong.