using VideoAnalysisCore.Common; using System.Net.Http.Headers; using System.Text; using Microsoft.Extensions.Logging; using Newtonsoft.Json.Linq; using System.Net.Http; using Newtonsoft.Json; using System.Net.Http.Json; using AntDesign; using OneOf.Types; using System.Net; using Azure; using System.Reflection.PortableExecutable; using static System.Runtime.InteropServices.JavaScript.JSType; /// /// https://platform.moonshot.cn/docs/api-reference /// namespace VideoAnalysisCore.AICore.ChatGPT.KIMI { 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) { var chatResp = await PostJsonStreamAsync("/v1/chat/completions", requestBody); return await chatResp.Content.ReadFromJsonAsync(); } /// /// ChatSSE /// /// /// Return HttpResponseMessage for SSE public async Task<(Usage u, string res)?> ChatSSE(ChatReq chatReq) { chatReq.stream = true; var requestBody = System.Text.Json.JsonSerializer.Serialize(chatReq); var chatResp = await PostJsonStreamAsync("/v1/chat/completions", requestBody); using var stream = await chatResp.Content.ReadAsStreamAsync(); using var reader = new StreamReader(stream, Encoding.UTF8); string line; StringBuilder messageBuilder = new StringBuilder(); ChatResSSE lastChat = new ChatResSSE(); while ((line = await reader.ReadLineAsync()) != null) { if (line.EndsWith("[DONE]")) { // 表示一条消息结束 string message = messageBuilder.ToString(); messageBuilder.Clear(); var u = lastChat?.choices?.FirstOrDefault()?.usage; if (u == null || string.IsNullOrEmpty(message)) return null; return (u, message); } else if (line.StartsWith("data:")) { try { var data = System.Text.Json.JsonSerializer.Deserialize(line.Substring("data:".Length).Trim()); lastChat = data; var str = data?.choices.FirstOrDefault()?.delta.content; if (!string.IsNullOrEmpty(str)) messageBuilder.Append(str); } catch (Exception e) { Console.WriteLine("异常 ChatSSE=>" ); Console.WriteLine(line); Console.WriteLine(e.Message); Console.WriteLine(e.StackTrace); } } } return null; } /// /// Chat /// /// /// Return HttpResponseMessage for SSE public async Task<(Usage u, string res)?> Chat(ChatReq chatReq) { var requestBody = System.Text.Json.JsonSerializer.Serialize(chatReq); var chatResp = await PostJsonStreamAsync("/v1/chat/completions", requestBody); var res = await chatResp.Content.ReadFromJsonAsync(); var chatResContent = res?.choices.FirstOrDefault()?.message.content.Trim(); if (res is null || res.error != null) throw new Exception($"KIMI模型返回异常 Chat 返回参数: " + $" {System.Text.Json.JsonSerializer.Serialize(res)}"); if (string.IsNullOrEmpty(chatResContent)) return null; return (res.usage, chatResContent); } /// /// 计算token长度 /// /// 文本 /// /// public async Task GetAsTiMateTokenCount(string chatReqText) { var reqObject = new { model = "moonshot-v1-128k", messages = new List() { new MessagesItem(chatReqText,"system"), } }; var response = await PostJsonStreamAsync("/v1/tokenizers/estimate-token-count", JsonConvert.SerializeObject(reqObject)); var responseText = await response.Content.ReadAsStringAsync(); if (response.IsSuccessStatusCode) { var responseObj = JToken.Parse(responseText); return responseObj?["data"]?["total_tokens"]?.ToObject(); } var error = JsonConvert.DeserializeObject(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 = JsonConvert.SerializeObject(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 uriBuilder = new UriBuilder(Host + path); var maxRestart = 4; var errorMSG = new Exception[maxRestart]; for (int i = 0; i < maxRestart; i++) { try { var client = _httpClientFactory.CreateClient(); client.Timeout = TimeSpan.FromSeconds(60 * 20);//超时时间20分钟 client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", ApiKey); client.DefaultRequestVersion = HttpVersion.Version20; client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrLower; client.DefaultRequestHeaders.ConnectionClose = true; //var request = ToHttpRequest(path); //request.Version = HttpVersion.Version20; //request.VersionPolicy = HttpVersionPolicy.RequestVersionOrLower; //request.Content = new StringContent(json, Encoding.UTF8, "application/json"); //return await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); var content = new StringContent(json, Encoding.UTF8, "application/json"); return await client.PostAsync(uriBuilder.Uri, content); } catch (Exception e) { errorMSG[i] = e; Console.WriteLine("====================[请求异常,重试]===================="); Console.WriteLine(uriBuilder.Uri); Console.WriteLine(e.Message); Console.WriteLine(e.StackTrace); Console.WriteLine("=============================================="); } Thread.Sleep(1000); } throw errorMSG.Last(s => s != null); } 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 JsonConvert.DeserializeObject(responseText) ?? default; } var error = JsonConvert.DeserializeObject(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; } } } }