Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
Odin Luo authored and Odin Luo committed Dec 20, 2023
0 parents commit 8272d5d
Show file tree
Hide file tree
Showing 12 changed files with 773 additions and 0 deletions.
48 changes: 48 additions & 0 deletions ConfigDto.cs
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; }
}
}
49 changes: 49 additions & 0 deletions ConfigHelper.cs
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();
}
}
}
}
56 changes: 56 additions & 0 deletions FileHelper.cs
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;
}
}
}
88 changes: 88 additions & 0 deletions MarkdownHelper.cs
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();
}
}
150 changes: 150 additions & 0 deletions NetworkHelper.cs
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;
}
}
}
Loading

0 comments on commit 8272d5d

Please sign in to comment.