using System.Text;
using System.Net.Http.Headers;
using Newtonsoft.Json;
namespace TestProject;
/// <summary>
/// API 클라이언트
/// </summary>
public class APIClient : IDisposable
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Event
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 메시지 도착시 이벤트 - MessageArrived
/// <summary>
/// 메시지 도착시 이벤트
/// </summary>
public event EventHandler<ResponseMessage> MessageArrived;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 리소스 해제 여부
/// </summary>
private bool disposed = false;
/// <summary>
/// BASE URL
/// </summary>
private const string BASE_URL = "http://localhost:11434";
/// <summary>
/// HTTP 클라이언트
/// </summary>
private HttpClient client;
/// <summary>
/// 품질을 갖는 스트림 미디어 타입 헤더 값
/// </summary>
private MediaTypeWithQualityHeaderValue streamMediaTypeWithQualityHeaderValue;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region HTTP 클라이언트 - Client
/// <summary>
/// HTTP 클라이언트
/// </summary>
public HttpClient Client
{
get
{
return this.client;
}
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region API 클라이언트 - APIClient()
/// <summary>
/// API 클라이언트
/// </summary>
public APIClient()
{
this.client = new HttpClient();
this.streamMediaTypeWithQualityHeaderValue = new MediaTypeWithQualityHeaderValue("text/event-stream");
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 채팅 요청하기 (비동기) - RequestChatAsync(requestMessage)
/// <summary>
/// 채팅 요청하기 (비동기)
/// </summary>
/// <param name="requestMessage">요청 메시지</param>
/// <returns>응답 메시지 태스크</returns>
public async Task<List<ResponseMessage>> RequestChatAsync(RequestMessage requestMessage)
{
if(this.client.DefaultRequestHeaders.Accept.Contains(this.streamMediaTypeWithQualityHeaderValue))
{
this.client.DefaultRequestHeaders.Accept.Remove(this.streamMediaTypeWithQualityHeaderValue);
}
string requestJSON = JsonConvert.SerializeObject(requestMessage);
StringContent content = new StringContent(requestJSON, Encoding.UTF8, "application/json");
HttpResponseMessage responseMessage = await this.client.PostAsync($"{BASE_URL}/api/generate", content);
responseMessage.EnsureSuccessStatusCode();
string responseString = await responseMessage.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<List<ResponseMessage>>($"[{responseString}]");
}
#endregion
#region 채팅 요청하기 (스트림) (비동기) - RequestChatStreamAsync(requestMessage)
/// <summary>
/// 채팅 요청하기 (스트림) (비동기)
/// </summary>
/// <param name="requestMessage">요청 메시지</param>
/// <returns>스트리밍 메시지 리스트 태스크</returns>
public async Task<List<ResponseMessage>> RequestChatStreamAsync(RequestMessage requestMessage)
{
if(!this.client.DefaultRequestHeaders.Accept.Contains(this.streamMediaTypeWithQualityHeaderValue))
{
this.client.DefaultRequestHeaders.Accept.Add(this.streamMediaTypeWithQualityHeaderValue);
}
string requestJSON = JsonConvert.SerializeObject(requestMessage);
StringContent content = new StringContent(requestJSON, Encoding.UTF8, "application/json");
using var request = new HttpRequestMessage(HttpMethod.Post, $"{BASE_URL}/api/generate");
request.Content = content;
using HttpResponseMessage httpResponseMessage = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
httpResponseMessage.EnsureSuccessStatusCode();
using Stream stream = await httpResponseMessage.Content.ReadAsStreamAsync();
using StreamReader reader = new StreamReader(stream);
List<ResponseMessage> responseMessageList = new List<ResponseMessage>();
while(!reader.EndOfStream)
{
string line = await reader.ReadLineAsync();
if(string.IsNullOrWhiteSpace(line))
{
continue;
}
ResponseMessage responseMessage = JsonConvert.DeserializeObject<ResponseMessage>(line);
responseMessageList.Add(responseMessage);
MessageArrived?.Invoke(this, responseMessage);
}
return responseMessageList;
}
#endregion
#region 리소스 해제하기 - Dispose()
/// <summary>
/// 리소스 해제하기
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Protected
#region 리소스 해제하기 - Dispose(disposing)
/// <summary>
/// 리소스 해제하기
/// </summary>
/// <param name="disposing">리소스 해제 여부</param>
protected virtual void Dispose(bool disposing)
{
if(!this.disposed)
{
if(disposing)
{
if(this.client != null)
{
this.client.Dispose();
}
this.client = null;
this.disposed = true;
}
}
}
#endregion
}