using VideoAnalysisCore.Common; using System.Net.Http.Headers; using System.Text; using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; using System.Net.Http; namespace VideoAnalysisCore.AICore.ChatGPT.KIMI { /// /// https://platform.moonshot.cn/docs/api-reference /// public class MoonshotClient { private readonly ILogger _logger; private readonly IHttpClientFactory _httpClientFactory; public MoonshotClient(ILogger logger, IHttpClientFactory httpClientFactory) { _logger = logger; _httpClientFactory = httpClientFactory; } /// /// list models /// /// public async Task ListModels() { var response = await GetAsync("/v1/models"); return await ParseResp(response); } /// /// Chat /// /// /// Return HttpResponseMessage for SSE public async Task Chat(string requestBody) { return await PostJsonStreamAsync("/v1/chat/completions", requestBody); } /// /// Chat /// /// /// Return HttpResponseMessage for SSE public async Task Chat(ChatReq chatReq) { var requestBody = System.Text.Json.JsonSerializer.Serialize(chatReq); return await PostJsonStreamAsync("/v1/chat/completions", requestBody); } /// /// Get as timate token count /// public async Task GetAsTiMateTokenCount(string chatReqText) { var response = await PostJsonAsync("/v1/tokenizers/estimate-token-count", chatReqText); var responseText = await response.Content.ReadAsStringAsync(); if (response.IsSuccessStatusCode) { var responseObj = JToken.Parse(responseText); return responseObj?["data"]?["total_tokens"]?.ToObject(); } var error = System.Text.Json.JsonSerializer.Deserialize(responseText); _logger.LogError($"{error?.error?.type}: {error?.error?.message}"); throw new Exception($"{error?.error.type}: {error?.error.message}"); } /// /// Get as timate token count /// /// /// public async Task GetAsTiMateTokenCount(ChatReq chatReq) { var chatReqText =System.Text.Json.JsonSerializer.Serialize(chatReq); return await GetAsTiMateTokenCount(chatReqText); } /// /// List files /// public virtual async Task ListFiles() { var response = await GetAsync("/v1/files"); return await ParseResp(response); } /// /// Upload file /// public virtual async Task UploadFile(string filePath) { if (!File.Exists(filePath)) { throw new FileNotFoundException($"{filePath} not found"); } var client = _httpClientFactory.CreateClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", ApiKey); var request = new HttpRequestMessage(HttpMethod.Post, $"{Host}/v1/files"); var content = new MultipartFormDataContent { { new StreamContent(File.OpenRead(filePath)), "file", filePath } }; request.Content = content; var response = await client.SendAsync(request); return await ParseResp(response); } /// /// Upload file stream /// public virtual async Task UploadFileStream(Stream stream, string fileName) { var client = _httpClientFactory.CreateClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", ApiKey); var request = new HttpRequestMessage(HttpMethod.Post, $"{Host}/v1/files"); var content = new MultipartFormDataContent { { new StreamContent(stream), "file", fileName } }; request.Content = content; var response = await client.SendAsync(request); return await ParseResp(response); } /// /// Get file content /// public virtual async Task GetFileContent(string fileId) { var response = await GetAsync($"/v1/files/{fileId}/content"); return await ParseResp(response); } private async Task GetAsync(string path) { var client = _httpClientFactory.CreateClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", ApiKey); return await client.GetAsync(Host + path); } private async Task PostJsonAsync(string path, string json) { var client = _httpClientFactory.CreateClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", ApiKey); return await client.PostAsync(Host + path, new StringContent(json, Encoding.UTF8, "application/json")); } private async Task PostJsonStreamAsync(string path, string json) { var client = _httpClientFactory.CreateClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", ApiKey); var request = ToHttpRequest(path); request.Content = new StringContent(json, Encoding.UTF8, "application/json"); return await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); } private HttpRequestMessage ToHttpRequest(string path) { var request = new HttpRequestMessage(); var uriBuilder = new UriBuilder(Host + path); request.RequestUri = uriBuilder.Uri; request.Method = new HttpMethod("POST"); request.Headers.Host = new Uri(Host).Host; return request; } /// /// Parse response /// private async Task ParseResp(HttpResponseMessage response) { var responseText = await response.Content.ReadAsStringAsync(); if (response.IsSuccessStatusCode) { return System.Text.Json.JsonSerializer.Deserialize(responseText) ?? default; } var error = System.Text.Json.JsonSerializer.Deserialize(responseText); _logger.LogError($"{error?.error.type}: {error?.error.message}"); throw new Exception($"{error?.error.type}: {error?.error.message}"); } private static string _host = "https://api.moonshot.cn"; public static string Host { get { if (string.IsNullOrEmpty(_host) && !string.IsNullOrEmpty(AppCommon.Config.ChatGpt.KIMI.Host)) { _host = AppCommon.Config.ChatGpt.KIMI.Host ?? ""; } return _host; } set { _host = value; } } private static string _apiKey = "sk_"; public static string ApiKey { get { if (string.IsNullOrEmpty(_apiKey) && !string.IsNullOrEmpty(AppCommon.Config.ChatGpt.KIMI.ApiKey)) { _apiKey = AppCommon.Config.ChatGpt.KIMI.ApiKey ?? ""; } return _apiKey; } set { _apiKey = value; } } } }