Compare commits

..

No commits in common. "42becf154a1a645d57d4d223297a96ea95f59866" and "d437ff0a46710f63faa74383ac390c1075830aa6" have entirely different histories.

15 changed files with 361 additions and 225 deletions

View File

@ -86,7 +86,12 @@ namespace Learn.VideoAnalysis
builder.Services.AddHttpClient(); builder.Services.AddHttpClient();
builder.Services.AddHttpContextAccessor(); builder.Services.AddHttpContextAccessor();
builder.Services.AddGPTService(); builder.Services.AddSingleton<ChatGPTClient>();
builder.Services.AddSingleton<DeepSeekGPTClient>();
//builder.Services.AddSingleton<IBserGPT, KIMI_GPT>();
//builder.Services.AddSingleton<IBserGPT, Chat_GPT>();
builder.Services.AddSingleton<IBserGPT, DeepSeek_GPT>();
builder.Services.AddTaskSubscribe(); builder.Services.AddTaskSubscribe();
var app = builder.Build(); var app = builder.Build();

View File

@ -64,7 +64,9 @@ onBeforeMount(() => {
}); });
}); });
onMounted(() => { onMounted(() => {
window.addEventListener("resize", appStyle); nextTick(async () => {
// appStyle();
});
}); });
onUnmounted(() => {}); onUnmounted(() => {});
// defineExpose({ // defineExpose({

View File

@ -12,15 +12,6 @@ export default {
rank: 0 rank: 0
}, },
children: [ children: [
{
path: "/welcome/runningTask",
name: "runningTask",
component: () => import("@/views/welcome/runningTask.vue"),
meta: {
title: "进行中任务",
showLink: true
}
},
{ {
path: "/welcome", path: "/welcome",
name: "Welcome", name: "Welcome",
@ -39,9 +30,18 @@ export default {
showLink: false showLink: false
} }
}, },
{
path: "/welcome/runningTask",
name: "runningTask",
component: () => import("@/views/welcome/runningTask.vue"),
meta: {
title: "进行中任务",
showLink: true
}
},
{ {
path: "/welcome/errorTask", path: "/welcome/errorTask",
name: "errorTask", name: "runningTask",
component: () => import("@/views/welcome/errorTask.vue"), component: () => import("@/views/welcome/errorTask.vue"),
meta: { meta: {
title: "错误任务", title: "错误任务",

View File

@ -44,13 +44,11 @@
"ChatGpt": { "ChatGpt": {
//"Host": "https://api.g4f.icu/", //"Host": "https://api.g4f.icu/",
"Host": "https://api.oaibest.com/", "Host": "https://api.oaibest.com/",
"ApiKey": "sk-D15tBln31N7dI9Fi7lds7OySFv5tOEK7DMNsG5rY2E6DCr4s", "ApiKey": "sk-D15tBln31N7dI9Fi7lds7OySFv5tOEK7DMNsG5rY2E6DCr4s"
"Path": "v1/chat/completions"
}, },
"DeepSeek": { "DeepSeek": {
"Host": "https://api.deepseek.com/", "Host": "https://api.deepseek.com/chat/completions",
"ApiKey": "sk-88d3d2bc3dae4d50854b2569b281cf76", "ApiKey": "sk-88d3d2bc3dae4d50854b2569b281cf76"
"Path": "chat/completions"
}, },
"aliyun": { "aliyun": {
"Host": "https://dashscope.aliyuncs.com/compatible-mode/", "Host": "https://dashscope.aliyuncs.com/compatible-mode/",

View File

@ -7,10 +7,6 @@ using VideoAnalysisCore.AICore.GPT.Dto;
using VideoAnalysisCore.AICore.SherpaOnnx; using VideoAnalysisCore.AICore.SherpaOnnx;
using VideoAnalysisCore.Common; using VideoAnalysisCore.Common;
using Whisper.net; using Whisper.net;
using Microsoft.Extensions.DependencyInjection;
using VideoAnalysisCore.AICore.GPT.ChatGPT;
using VideoAnalysisCore.AICore.GPT.DeepSeek;
using System.Text.Json.Serialization;
namespace VideoAnalysisCore.AICore.GPT namespace VideoAnalysisCore.AICore.GPT
{ {
@ -52,8 +48,6 @@ namespace VideoAnalysisCore.AICore.GPT
/// <para>此功能处于 Beta 阶段。 如果指定,我们的系统将尽最大努力确定性地采样,这样具有相同 and 参数的重复请求应该返回相同的结果。 无法保证确定性,您应该参考 response 参数来监控后端的变化</para> /// <para>此功能处于 Beta 阶段。 如果指定,我们的系统将尽最大努力确定性地采样,这样具有相同 and 参数的重复请求应该返回相同的结果。 无法保证确定性,您应该参考 response 参数来监控后端的变化</para>
/// </summary> /// </summary>
public int? seed { get; set; } = null; public int? seed { get; set; } = null;
[JsonIgnore]
public string? title { get; set; } = null;
/// <summary> /// <summary>
/// 推理模型 (deepseek-reasoner) /// 推理模型 (deepseek-reasoner)
/// </summary> /// </summary>
@ -179,21 +173,47 @@ namespace VideoAnalysisCore.AICore.GPT
public string reasoning_content { get; set; } public string reasoning_content { get; set; }
public string refusal { get; set; } public string refusal { get; set; }
} }
public static class GPTHttpClientExp
public static class GPTExpand
{ {
/// <summary> public static async Task<HttpResponseMessage> PostJsonStreamAsync(
/// 注册GPT服务 this IHttpClientFactory _httpClientFactory,
/// </summary> string path, string json,string apiKey, bool readAll = false)
/// <param name="services"></param>
public static void AddGPTService(this IServiceCollection services)
{ {
services.AddSingleton<DeepSeekGPTClient>(); var uriBuilder = new UriBuilder(path);
services.AddSingleton<ChatGPTClient>(); var maxRestart = 4;
services.AddSingleton<IBserGPT, DeepSeek_GPT>(); var errorMSG = new Exception[maxRestart];
for (int i = 0; i < maxRestart; i++)
{
try
{
var client = _httpClientFactory.CreateClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", apiKey);
client.Timeout = TimeSpan.FromSeconds(60 * 20);//超时时间20分钟
client.DefaultRequestVersion = HttpVersion.Version20;
client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrLower;
client.DefaultRequestHeaders.ConnectionClose = true;
var request = new HttpRequestMessage(HttpMethod.Post, uriBuilder.Uri);
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
if (readAll)
return await client.SendAsync(request);
return await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
} }
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);
}
} }
} }

View File

@ -15,21 +15,94 @@ using System.Text.Json;
namespace VideoAnalysisCore.AICore.GPT.ChatGPT namespace VideoAnalysisCore.AICore.GPT.ChatGPT
{ {
public class ChatGPTClient : GPTClient public class ChatGPTClient
{ {
public static string Host = AppCommon.Config.ChatGpt.ChatGpt.Host;
public override GptConfig Config { get; set; } = AppCommon.Config.ChatGpt.ChatGpt; public static string ApiKey = AppCommon.Config.ChatGpt.ChatGpt.ApiKey;
private readonly IHttpClientFactory _httpClientFactory; private readonly IHttpClientFactory _httpClientFactory;
private readonly RedisManager redisManager; private readonly RedisManager redisManager;
public ChatGPTClient(IHttpClientFactory httpClientFactory, RedisManager redisManager) : base(httpClientFactory, redisManager) public ChatGPTClient(IHttpClientFactory httpClientFactory, RedisManager redisManager)
{ {
_httpClientFactory = httpClientFactory; _httpClientFactory = httpClientFactory;
this.redisManager = redisManager; this.redisManager = redisManager;
} }
/// <summary>
/// ChatSSE[流式传输 更稳定]
/// </summary>
/// <param name="chatReq"></param>
/// <returns>Return HttpResponseMessage for SSE</returns>
public async Task<(Usage u, string res)?> ChatSSE(ChatRequest chatReq)
{
var requestBody = chatReq.ToJson();
PostJsonStream:
var chatResp = await _httpClientFactory.PostJsonStreamAsync(Host+"v1/chat/completions", requestBody, ApiKey);
if (!chatResp.IsSuccessStatusCode)
{
await redisManager.AddTaskLog(chatReq.taskId, "请求GPT服务器异常 " + chatResp?.StatusCode + await chatResp.Content.ReadAsStringAsync());
goto PostJsonStream;
}
using var stream = chatResp.Content.ReadAsStream();
using var reader = new StreamReader(stream, Encoding.UTF8);
string line;
var messageBuilder = new StringBuilder();
var messageBuilder1 = new StringBuilder();
var lastChat = new ChatResSSE();
var splitCount = "data:".Length;
var maxLoop = 60 * 100000;
int threshold = 0;
while (maxLoop > 0)
{
line = reader.ReadLine();
if (line is null || string.IsNullOrEmpty(line) || line.StartsWith(": keep-alive"))
{
Thread.Sleep(10);
maxLoop--;
continue;
}
else if (line.EndsWith("[DONE]"))
{
// 表示一条消息结束
string message = messageBuilder.ToString();
string message2 = messageBuilder1.ToString();
messageBuilder.Clear();
messageBuilder1.Clear();
var u = lastChat?.usage;
if (u == null || string.IsNullOrEmpty(message))
return null;
return (u, message);
//return (u, message, message2);
}
else if (line.StartsWith("data:"))
{
try
{
var data = System.Text.Json.JsonSerializer.Deserialize<ChatResSSE>(line.Substring(splitCount).Trim());
lastChat = data;
var delta = data?.choices.FirstOrDefault()?.delta;
var str = delta?.content;
var strReasoning = delta?.reasoning_content;
if (!string.IsNullOrEmpty(str))
messageBuilder.Append(str);
if (!string.IsNullOrEmpty(strReasoning))
messageBuilder1.Append(strReasoning);
var steamCount = messageBuilder.Length + messageBuilder1.Length;
if (++threshold % 30 == 0)
redisManager.SetTaskProgress(chatReq.taskId, "steam=>" + steamCount);
}
catch (Exception e)
{
await redisManager.AddTaskLog(chatReq.taskId, "异常 ChatSSE=>" + line + "\r\n" + e.Message + "\r\n" + e.StackTrace);
}
}
}
await redisManager.AddTaskLog(chatReq.taskId, DateTime.Now + "=>AI请求超时 " + chatReq.taskId);
return null;
}
/// <summary> /// <summary>
/// 请求AI /// 请求AI
/// </summary> /// </summary>
@ -41,29 +114,101 @@ namespace VideoAnalysisCore.AICore.GPT.ChatGPT
/// <param name="max_tokens">最大token <para>不设置默认最大值 16000/8000</para></param> /// <param name="max_tokens">最大token <para>不设置默认最大值 16000/8000</para></param>
/// <returns></returns> /// <returns></returns>
/// <exception cref="Exception"></exception> /// <exception cref="Exception"></exception>
public async Task<T> ChatAsync<T>(string task, string postMessages, string title, string model = null, int max_tokens = 8000) public async Task<T> ChatAsync<T>(string task, string postMessages, string title, string model =null, int max_tokens = -1)
{ {
Message[] messageArr = [ Message[] messageArr = [
new Message(postMessages,"user"), new Message(postMessages,"user"),
]; ];
model = model ?? ChatGPTType.GPT5_mini;
messageArr = messageArr.Where(s => s != null).ToArray(); messageArr = messageArr.Where(s => s != null).ToArray();
var chatReq = new ChatRequest var chatRep = new ChatRequest
{ {
taskId = task, taskId = task,
model = model, model = model ?? ChatGPTType.GPT5_mini,
max_tokens =8000, max_tokens = 8000,
stream = true, stream = true,
temperature = 0.2f, temperature = 0.2f,
messages = messageArr messages = messageArr
}; };
if (max_tokens != -1)
chatRep.max_tokens = max_tokens;
var tryCount = 10;
while (tryCount-- > 0)
{
try
{
var time = title + DateTime.Now.ToString("MMddHHmmss");
var redisCached = new object[2] { chatRep, null };
redisManager.SetTaskGPTCached(task, time, chatRep);
var chatResp = await Chat(chatRep);
var chatResContent = chatResp?.res;
if (string.IsNullOrEmpty(chatResContent))
throw new Exception("GPT返回message无效结果");
if (chatResp != null)
{
redisCached[1] = new object[] { chatResp.Value.res, chatResp.Value.u, chatResp.Value };
redisManager.SetTaskGPTCached(task, time, redisCached);
}
chatResContent = chatResContent?.ExtractJsonStrings()?.FirstOrDefault();
chatResContent = chatResContent?.Replace("\n", "");
chatResContent = chatResContent?.Replace("```json", "");
chatResContent = chatResContent?.Replace("```", "");
chatResContent = chatResContent?.Replace("}{", "},{");
chatResContent = chatResContent?.Replace("}|{", "},{");
chatResContent = chatResContent?.Trim();
chatReq.modalities = null; if (string.IsNullOrEmpty(chatResContent))
throw new Exception("ChatGPT返回结果无有效JSON");
var startsStr = typeof(T).IsArray ? "[" : "{";
var endStr = typeof(T).IsArray ? "]" : "}";
if (!chatResContent.StartsWith(startsStr))
chatResContent = startsStr + chatResContent;
if (!chatResContent.EndsWith(endStr))
chatResContent = chatResContent + endStr;
var options = new JsonSerializerOptions
{
// 允许解析不严格符合 JSON 规范的字符串
AllowTrailingCommas = true,
// 处理不匹配的 JSON 字符
ReadCommentHandling = JsonCommentHandling.Skip
};
var questionRes = System.Text.Json.JsonSerializer.Deserialize<T>(chatResContent, options);
if (questionRes is null)
throw new Exception("ChatGPT返回无效结果");
return questionRes;
}
catch (Exception ex)
{
await redisManager.AddTaskLog(task, $"ChatGPT结果解析错误 重试剩余{tryCount}" + ex.Message);
}
}
throw new Exception(DateTime.Now + "=>ChatGPT请求失败次数过多!!!");
}
/// <summary>
/// Chat
/// </summary>
/// <param name="chatReq"></param>
/// <returns>Return HttpResponseMessage for SSE</returns>
public async Task<(Usage u, string res)?> Chat(ChatRequest chatReq)
{
chatReq.modalities =null;
chatReq.max_tokens = null; chatReq.max_tokens = null;
chatReq.top_p = null; chatReq.top_p = null;
if (chatReq.stream) return await ChatSSE(chatReq);
return await base.ChatAsync<T>(chatReq); var requestBody = chatReq.ToJson();
var chatResp = await _httpClientFactory.PostJsonStreamAsync(Host+"v1/chat/completions", requestBody,ApiKey,true);
var res = await chatResp.Content.ReadFromJsonAsync<ChatRes>();
var chatResContent = res?.choices.FirstOrDefault()?.message.content.Trim();
if (res is null || res.error != null)
throw new Exception($" ChatGPT模型返回异常 返回参数: " +
$" {res?.ToJson()}");
if (string.IsNullOrEmpty(chatResContent))
return null;
return (res.usage, chatResContent);
} }
} }
} }

View File

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VideoAnalysisCore.AICore.GPT.ChatGPT
{
}

View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VideoAnalysisCore.AICore.GPT.ChatGPT
{
public class ChatGPTType
{
public static string GPT5_mini = "gpt-5-mini-2025-08-07";
public static string GPT5 = "gpt-5-2025-08-07";
public static string GPT5_nano = "gpt-5-nano-2025-08-07";
/// <summary>
/// o1 系列模型通过强化学习进行训练以执行复杂的推理。o1 模型在回答之前会思考,在回应用户之前会产生一个漫长的内部思维链。在我们的推理指南中了解 o1 模型的功能。
///<para>目前有两种型号可供选择:</para>
///<para>O1旨在解决跨领域的难题的推理模型</para>
///<para>O1-Mini用于专业任务的快速且经济实惠的推理模型</para>
/// </summary>
public static string GPTo1 = "o1";
/// <summary>
/// o1 系列模型通过强化学习进行训练以执行复杂的推理。o1 模型在回答之前会思考,在回应用户之前会产生一个漫长的内部思维链。在我们的推理指南中了解 o1 模型的功能。
///<para>目前有两种型号可供选择:</para>
///<para>O1旨在解决跨领域的难题的推理模型</para>
///<para>O1-Mini用于专业任务的快速且经济实惠的推理模型</para>
/// </summary>
public static string GPTo1Mini = "o1-mini";
}
}

View File

@ -1,20 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VideoAnalysisCore.AICore.GPT
{
public class ChatGPTType
{
public static string GPT5_mini = "gpt-5-mini-2025-08-07";
public static string GPT5 = "gpt-5-2025-08-07";
public static string GPT5_nano = "gpt-5-nano-2025-08-07";
public static string Deepseek_Reasoner = "deepseek-reasoner";
public static string Deepseek_Chat = "deepseek-chat";
}
}

View File

@ -11,36 +11,71 @@ using System.Threading;
using System; using System;
using System.IO; using System.IO;
using VideoAnalysisCore.AICore.GPT.ChatGPT; using VideoAnalysisCore.AICore.GPT.ChatGPT;
using VideoAnalysisCore.AICore.GPT;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Text.Json; using System.Text.Json;
namespace VideoAnalysisCore.AICore.GPT namespace VideoAnalysisCore.AICore.GPT.DeepSeek
{ {
public abstract class GPTClient
public class DeepSeekGPTClient
{ {
public virtual GptConfig Config { get; set; }
//private readonly string Path = "v1/chat/completions";
//public static string Host = AppCommon.Config.ChatGpt.aliyun.Host;
//public static string ApiKey = AppCommon.Config.ChatGpt.aliyun.ApiKey;
private readonly string Path = "";
public static string Host = AppCommon.Config.ChatGpt.DeepSeek.Host;
public static string ApiKey = AppCommon.Config.ChatGpt.DeepSeek.ApiKey;
//public static string Host = AppCommon.Config.ChatGpt.ChatGpt.Host;
//public static string ApiKey = AppCommon.Config.ChatGpt.ChatGpt.ApiKey;
//private readonly string Path = "v1/chat/completions";
private readonly IHttpClientFactory _httpClientFactory; private readonly IHttpClientFactory _httpClientFactory;
private readonly RedisManager redisManager; private readonly RedisManager redisManager;
public GPTClient(IHttpClientFactory httpClientFactory, RedisManager redisManager) public DeepSeekGPTClient(IHttpClientFactory httpClientFactory, RedisManager redisManager)
{ {
_httpClientFactory = httpClientFactory; _httpClientFactory = httpClientFactory;
this.redisManager = redisManager; this.redisManager = redisManager;
} }
/// <summary> /// <summary>
/// Chat /// Chat
/// </summary> /// </summary>
/// <param name="chatReq"></param> /// <param name="chatReq"></param>
/// <returns>Return HttpResponseMessage for SSE</returns> /// <returns>Return HttpResponseMessage for SSE</returns>
public async Task<(Usage u, string res, string reasoning)?> Chat(ChatRequest chatReq) public async Task<(Usage u, string res,string reasoning)?> Chat(ChatRequest chatReq)
{ {
//chatReq.model = "deepseek-r1"; //chatReq.model = "deepseek-r1";
if (chatReq.stream) return await ChatSSE(chatReq); if (chatReq.stream) return await ChatSSE(chatReq);
throw new NotImplementedException();
postStar:
var requestBody = chatReq.ToJson();
HttpResponseMessage chatResp = await _httpClientFactory.PostJsonStreamAsync(Host+Path, requestBody, ApiKey,true);
var res1 = await chatResp.Content.ReadAsStringAsync();
if (res1 == null || string.IsNullOrEmpty(res1)|| !chatResp.IsSuccessStatusCode)
{
await redisManager.AddTaskLog(chatReq.taskId,$"=>GPT请求失败重试 Code = {chatResp.StatusCode} Res={res1}");
goto postStar;
}
await redisManager.AddTaskLog(chatReq.taskId, $"=>GPT请求头获取成功 Code = {chatResp.StatusCode} Res={res1}");
var res = await chatResp.Content.ReadFromJsonAsync<ChatRes>();
if (res is null || res.error != null)
throw new Exception($" GPT模型返回异常 返回参数: " +
$" {res.ToJson()}");
var d = thinkMSG(res?.choices.FirstOrDefault()?.message);
var chatResContent = d.m1;
var chatResReasoning = d.m2;
if (string.IsNullOrEmpty(chatResContent))
return null;
return (res.usage, chatResContent, chatResReasoning);
} }
private (string m1, string m2) thinkMSG(Message? m) private (string m1, string m2) thinkMSG(Message? m)
@ -59,6 +94,9 @@ namespace VideoAnalysisCore.AICore.GPT
} }
/// <summary> /// <summary>
/// ChatSSE[流式传输 更稳定] /// ChatSSE[流式传输 更稳定]
/// </summary> /// </summary>
@ -67,16 +105,11 @@ namespace VideoAnalysisCore.AICore.GPT
public async Task<(Usage u, string res, string reasoning)?> ChatSSE(ChatRequest chatReq) public async Task<(Usage u, string res, string reasoning)?> ChatSSE(ChatRequest chatReq)
{ {
var requestBody = chatReq.ToJson(); var requestBody = chatReq.ToJson();
var i = 5;
PostJsonStream: PostJsonStream:
var chatResp = await PostJsonStreamAsync(Config.Host + Config.Path, requestBody, Config.ApiKey); var chatResp = await _httpClientFactory.PostJsonStreamAsync(Host, requestBody,ApiKey);
if (!chatResp.IsSuccessStatusCode) if (!chatResp.IsSuccessStatusCode)
{ {
await redisManager.AddTaskLog(chatReq.taskId, "=>请求GPT服务器异常 " + chatResp?.StatusCode + " " + await chatResp.Content.ReadAsStringAsync()); await redisManager.AddTaskLog(chatReq.taskId,"=>请求GPT服务器异常 " + chatResp?.StatusCode +" "+ await chatResp.Content.ReadAsStringAsync());
if (--i < 0)
{
throw new Exception("请求GPT服务器失败次数过多");
}
goto PostJsonStream; goto PostJsonStream;
} }
using var stream = chatResp.Content.ReadAsStream(); using var stream = chatResp.Content.ReadAsStream();
@ -86,17 +119,13 @@ namespace VideoAnalysisCore.AICore.GPT
var messageBuilder1 = new StringBuilder(); var messageBuilder1 = new StringBuilder();
var lastChat = new ChatResSSE(); var lastChat = new ChatResSSE();
var splitCount = "data:".Length; var splitCount = "data:".Length;
var maxLoop = 50 * 200; var maxLoop = 60*100000;
int threshold = 0; int threshold = 0;
var startTime= DateTime.Now; while (maxLoop>0)
var endTime = startTime.AddHours(1.5);
//最长分析分析时间1.5小时 或者重试读取 1w次
while (maxLoop > 0 && DateTime.Now < endTime)
{ {
line = reader.ReadLine(); line = reader.ReadLine();
if (line is null || string.IsNullOrEmpty(line) || line.StartsWith(": keep-alive")) if (line is null || string.IsNullOrEmpty(line)|| line.StartsWith(": keep-alive")) {
{ Thread.Sleep(10);
Thread.Sleep(50);
maxLoop--; maxLoop--;
continue; continue;
} }
@ -107,7 +136,7 @@ namespace VideoAnalysisCore.AICore.GPT
string message2 = messageBuilder1.ToString(); string message2 = messageBuilder1.ToString();
messageBuilder.Clear(); messageBuilder.Clear();
messageBuilder1.Clear(); messageBuilder1.Clear();
var d = thinkMSG(new Message() { content = message, reasoning_content = message2 }); var d =thinkMSG(new Message() { content = message, reasoning_content = message2 });
message = d.m1; message = d.m1;
message2 = d.m2; message2 = d.m2;
var u = lastChat?.usage; var u = lastChat?.usage;
@ -129,8 +158,8 @@ namespace VideoAnalysisCore.AICore.GPT
if (!string.IsNullOrEmpty(strReasoning)) if (!string.IsNullOrEmpty(strReasoning))
messageBuilder1.Append(strReasoning); messageBuilder1.Append(strReasoning);
var steamCount = messageBuilder.Length + messageBuilder1.Length; var steamCount = messageBuilder.Length + messageBuilder1.Length;
if (++threshold % 30 == 0) if (++threshold%30==0)
redisManager.SetTaskProgress(chatReq.taskId, "steam=>" + steamCount); redisManager.SetTaskProgress(chatReq.taskId, "steam=>"+ steamCount);
} }
catch (Exception e) catch (Exception e)
{ {
@ -142,6 +171,7 @@ namespace VideoAnalysisCore.AICore.GPT
return null; return null;
} }
/// <summary> /// <summary>
/// 请求AI /// 请求AI
/// </summary> /// </summary>
@ -153,16 +183,31 @@ namespace VideoAnalysisCore.AICore.GPT
/// <param name="max_tokens">最大token <para>不设置默认最大值 16000/8000</para></param> /// <param name="max_tokens">最大token <para>不设置默认最大值 16000/8000</para></param>
/// <returns></returns> /// <returns></returns>
/// <exception cref="Exception"></exception> /// <exception cref="Exception"></exception>
public async Task<T> ChatAsync<T>(ChatRequest chatRep) public async Task<T> ChatAsync<T>(string task, string postMessages, string title, string model = "deepseek-reasoner", int max_tokens = -1)
{ {
Message[] messageArr = [
new Message(postMessages,"user"),
];
messageArr = messageArr.Where(s => s != null).ToArray();
var chatRep = new ChatRequest
{
taskId = task,
model = model,
max_tokens = model == "deepseek-reasoner" ? 16000 : 8000,
stream = true,
temperature = 0.2f,
messages = messageArr
};
if (max_tokens != -1)
chatRep.max_tokens = max_tokens;
var tryCount = 10; var tryCount = 10;
while (tryCount-- > 0) while (tryCount-- > 0)
{ {
try try
{ {
var time = chatRep.title + DateTime.Now.ToString("MMddHHmmss"); var time = title + DateTime.Now.ToString("MMddHHmmss");
var redisCached = new object[2] { chatRep, null }; var redisCached = new object[2] { chatRep, null };
redisManager.SetTaskGPTCached(chatRep.taskId, time, chatRep); redisManager.SetTaskGPTCached(task, time, chatRep);
var chatResp = await Chat(chatRep); var chatResp = await Chat(chatRep);
var chatResContent = chatResp?.res; var chatResContent = chatResp?.res;
if (string.IsNullOrEmpty(chatResContent)) if (string.IsNullOrEmpty(chatResContent))
@ -170,7 +215,7 @@ namespace VideoAnalysisCore.AICore.GPT
if (chatResp != null) if (chatResp != null)
{ {
redisCached[1] = new object[] { chatResp.Value.res, chatResp.Value.u, chatResp.Value.reasoning }; redisCached[1] = new object[] { chatResp.Value.res, chatResp.Value.u, chatResp.Value.reasoning };
redisManager.SetTaskGPTCached(chatRep.taskId, time, redisCached); redisManager.SetTaskGPTCached(task, time, redisCached);
} }
chatResContent = chatResContent?.ExtractJsonStrings()?.FirstOrDefault(); chatResContent = chatResContent?.ExtractJsonStrings()?.FirstOrDefault();
chatResContent = chatResContent?.Replace("\n", ""); chatResContent = chatResContent?.Replace("\n", "");
@ -181,7 +226,7 @@ namespace VideoAnalysisCore.AICore.GPT
chatResContent = chatResContent?.Trim(); chatResContent = chatResContent?.Trim();
if (string.IsNullOrEmpty(chatResContent)) if (string.IsNullOrEmpty(chatResContent))
throw new Exception("GPT返回结果无有效JSON"); throw new Exception("ChatGPT返回结果无有效JSON");
var startsStr = typeof(T).IsArray ? "[" : "{"; var startsStr = typeof(T).IsArray ? "[" : "{";
var endStr = typeof(T).IsArray ? "]" : "}"; var endStr = typeof(T).IsArray ? "]" : "}";
if (!chatResContent.StartsWith(startsStr)) if (!chatResContent.StartsWith(startsStr))
@ -197,54 +242,17 @@ namespace VideoAnalysisCore.AICore.GPT
}; };
var questionRes = System.Text.Json.JsonSerializer.Deserialize<T>(chatResContent, options); var questionRes = System.Text.Json.JsonSerializer.Deserialize<T>(chatResContent, options);
if (questionRes is null) if (questionRes is null)
throw new Exception("GPT返回无效结果"); throw new Exception("ChatGPT返回无效结果");
return questionRes; return questionRes;
} }
catch (Exception ex) catch (Exception ex)
{ {
await redisManager.AddTaskLog(chatRep.taskId, $"=>GPT结果解析错误 重试剩余{tryCount} {ex.Message}"); await redisManager.AddTaskLog(task, $"=>ChatGPT结果解析错误 重试剩余{tryCount} {ex.Message}");
} }
} }
await redisManager.AddTaskLog(chatRep.taskId, $"=>GPT请求失败次数过多!!!"); await redisManager.AddTaskLog(task, $"=>ChatGPT请求失败次数过多!!!");
throw new Exception(DateTime.Now + "=>GPT请求失败次数过多!!!"); throw new Exception(DateTime.Now + "=>ChatGPT请求失败次数过多!!!");
} }
public async Task<HttpResponseMessage> PostJsonStreamAsync(
string path, string json, string apiKey, bool readAll = false)
{
var uriBuilder = new UriBuilder(path);
var maxRestart = 4;
var errorMSG = new Exception[maxRestart];
for (int i = 0; i < maxRestart; i++)
{
try
{
var client = _httpClientFactory.CreateClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", apiKey);
client.Timeout = TimeSpan.FromSeconds(60 * 20);//超时时间20分钟
client.DefaultRequestVersion = HttpVersion.Version20;
client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrLower;
client.DefaultRequestHeaders.ConnectionClose = true;
var request = new HttpRequestMessage(HttpMethod.Post, uriBuilder.Uri);
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
if (readAll)
return await client.SendAsync(request);
return await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
}
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);
}
} }
} }

View File

@ -1,64 +0,0 @@
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 System.Net;
using VideoAnalysisCore.AICore.GPT.DeepSeek;
using VideoAnalysisCore.AICore.GPT;
using System.Text.Json;
namespace VideoAnalysisCore.AICore.GPT.ChatGPT
{
public class DeepSeekGPTClient : GPTClient
{
public override GptConfig Config { get; set; } = AppCommon.Config.ChatGpt.DeepSeek;
private readonly IHttpClientFactory _httpClientFactory;
private readonly RedisManager redisManager;
public DeepSeekGPTClient(IHttpClientFactory httpClientFactory, RedisManager redisManager)
:base(httpClientFactory, redisManager)
{
_httpClientFactory = httpClientFactory;
this.redisManager = redisManager;
}
/// <summary>
/// 请求AI
/// </summary>
/// <typeparam name="T">返回JSON类型</typeparam>
/// <param name="task">任务id</param>
/// <param name="postMessages">提示词</param>
/// <param name="title">任务类型</param>
/// <param name="model">GPT版本</param>
/// <param name="max_tokens">最大token <para>不设置默认最大值 16000/8000</para></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<T> ChatAsync<T>(string task, string postMessages, string title, string model =null, int max_tokens = 8000)
{
Message[] messageArr = [
new Message(postMessages,"user"),
];
messageArr = messageArr.Where(s => s != null).ToArray();
var chatReq = new ChatRequest
{
taskId = task,
model = model ?? ChatGPTType.Deepseek_Reasoner,
max_tokens = model == ChatGPTType.Deepseek_Reasoner ? 16000 : max_tokens,
stream = true,
temperature = 0.2f,
messages = messageArr
};
return await base.ChatAsync<T>(chatReq);
}
}
}

View File

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VideoAnalysisCore.AICore.GPT.DeepSeek
{
}

View File

@ -133,10 +133,6 @@ namespace VideoAnalysisCore.Common
/// api的密钥 /// api的密钥
/// </summary> /// </summary>
public string ApiKey { get; set; } = string.Empty; public string ApiKey { get; set; } = string.Empty;
/// <summary>
/// API的路径
/// </summary>
public string Path { get; set; } = string.Empty;
} }
/// <summary> /// <summary>
/// 文本模型 配置 /// 文本模型 配置

View File

@ -215,9 +215,6 @@ namespace VideoAnalysisCore.Common
/// <param name="msg">内容</param> /// <param name="msg">内容</param>
public async Task AddTaskLog(object taskId, string msg) public async Task AddTaskLog(object taskId, string msg)
{ {
#if DEBUG
Console.WriteLine($"{DateTime.Now.ToString("MM-dd HH:mm:ss")} => {taskId} \r\n{msg}\r\n");
#endif
await Redis.RPushAsync(RedisExpandKey.TaskLog, await Redis.RPushAsync(RedisExpandKey.TaskLog,
new TaskLog() new TaskLog()
{ {
@ -225,18 +222,17 @@ namespace VideoAnalysisCore.Common
CreateTime = DateTime.Now, CreateTime = DateTime.Now,
Message = msg Message = msg
}); });
var count = 50;
lock (RedisExpandKey.TaskLog) lock (RedisExpandKey.TaskLog)
{ {
var oldTaskCount = Redis.LLen(RedisExpandKey.TaskLog); var oldTaskCount = Redis.LLen(RedisExpandKey.TaskLog);
if (oldTaskCount > count) if (oldTaskCount > 100)
{ {
try try
{ {
var insertData = Redis.LRange<TaskLog>(RedisExpandKey.TaskLog, 0, count -1); var insertData = Redis.LRange<TaskLog>(RedisExpandKey.TaskLog, 0, 99);
taskLogDB.AsInsertable(insertData).ExecuteCommand(); taskLogDB.AsInsertable(insertData).ExecuteCommand();
//同步删除redis //同步删除redis
Redis.LTrim(RedisExpandKey.TaskLog, count, 1000); Redis.LTrim(RedisExpandKey.TaskLog, 100, 1000);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -26,9 +26,7 @@ namespace VideoAnalysisCore.Model.Enum
[Description("班会")] [Description("班会")]
= 5, = 5,
[Description("其他资料")] [Description("其他资料")]
= 7, = 7
[Description("教研")]
= 8
} }
} }