From c8d606a16d481891743ccda5a811d2f9a5f5bdda Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Mon, 13 May 2024 21:50:51 +0800 Subject: [PATCH 01/30] Beta115 (#113) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 --- applogic/gensokyo.go | 55 ++++++++++++++++++++++++++++++++------------ applogic/hunyuan.go | 3 ++- 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/applogic/gensokyo.go b/applogic/gensokyo.go index 246c80c..71369e8 100644 --- a/applogic/gensokyo.go +++ b/applogic/gensokyo.go @@ -11,6 +11,7 @@ import ( "net/url" "strconv" "strings" + "sync" "github.com/hoshinonyaruko/gensokyo-llm/acnode" "github.com/hoshinonyaruko/gensokyo-llm/config" @@ -20,7 +21,8 @@ import ( ) var newmsgToStringMap = make(map[string]string) -var stringToIndexMap = make(map[string]int) +var stringToIndexMap sync.Map +var processMessageMu sync.Mutex // RecordStringById 根据id记录一个string func RecordStringByNewmsg(id, value string) { @@ -38,20 +40,30 @@ func GetStringByNewmsg(newmsg string) string { // IncrementIndex 为给定的字符串递增索引 func IncrementIndex(s string) int { - // 检查map中是否已经有这个字符串的索引 - if _, exists := stringToIndexMap[s]; !exists { - // 如果不存在,初始化为0 - stringToIndexMap[s] = 0 + // 尝试从map中获取值,如果不存在则初始化为0 + val, loaded := stringToIndexMap.LoadOrStore(s, 0) + if !loaded { + // 如果这是一个新的键,我们现在将其值设置为1 + stringToIndexMap.Store(s, 1) + return 1 } - // 递增索引 - stringToIndexMap[s]++ - // 返回新的索引值 - return stringToIndexMap[s] + + // 如果已存在,递增索引 + newVal := val.(int) + 1 + stringToIndexMap.Store(s, newVal) + return newVal } -// ResetIndex 将给定字符串的索引归零 +// ResetIndex 重置或删除给定字符串的索引 func ResetIndex(s string) { - stringToIndexMap[s] = 0 + // 直接从map中删除指定的键 + stringToIndexMap.Delete(s) +} + +// ResetAllIndexes 清空整个索引map +func ResetAllIndexes() { + // 重新初始化stringToIndexMap,因为sync.Map没有提供清空所有条目的直接方法 + stringToIndexMap = sync.Map{} } // checkMessageForHints 检查消息中是否包含给定的提示词 @@ -607,7 +619,11 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { lastMessageID = id // 更新lastMessageID // 检查是否有未发送的消息部分 key := utils.GetKey(message.GroupID, message.UserID) - accumulatedMessage, exists := groupUserMessages[key] + accumulatedMessageInterface, exists := groupUserMessages.Load(key) + var accumulatedMessage string + if exists { + accumulatedMessage = accumulatedMessageInterface.(string) + } // 提取response字段 if response, ok = responseData["response"].(string); ok { @@ -721,8 +737,8 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } } - // 清空映射中对应的累积消息 - groupUserMessages[key] = "" + // 清空key的值 + groupUserMessages.Store(key, "") } } else { //发送信息 @@ -909,7 +925,16 @@ func processMessage(response string, msg structs.OnebotGroupMessage, newmesssage // 达到标点符号,发送累积的整个消息 if messageBuilder.Len() > 0 { accumulatedMessage := messageBuilder.String() - groupUserMessages[key] += accumulatedMessage + // 锁定 + processMessageMu.Lock() + // 从sync.map读取当前的value + valueInterface, _ := groupUserMessages.Load(key) + value, _ := valueInterface.(string) + // 添加当前messageBuilder中的新内容 + value += accumulatedMessage + // 储存新的内容到sync.map + groupUserMessages.Store(key, value) + processMessageMu.Unlock() // 完成更新后时解锁 // 判断消息类型,如果是私人消息或私有群消息,发送私人消息;否则,根据配置决定是否发送群消息 if msg.RealMessageType == "group_private" || msg.MessageType == "private" { diff --git a/applogic/hunyuan.go b/applogic/hunyuan.go index 59dc088..8168e87 100644 --- a/applogic/hunyuan.go +++ b/applogic/hunyuan.go @@ -5,6 +5,7 @@ import ( "fmt" "net/http" "strings" + "sync" "github.com/hoshinonyaruko/gensokyo-llm/config" "github.com/hoshinonyaruko/gensokyo-llm/fmtf" @@ -15,7 +16,7 @@ import ( ) var messageBuilder strings.Builder -var groupUserMessages = make(map[string]string) +var groupUserMessages sync.Map func (app *App) ChatHandlerHunyuan(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { From d23d5239704c1f0a1ab1c4a795b09463d037a843 Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Tue, 14 May 2024 18:22:59 +0800 Subject: [PATCH 02/30] Beta116 (#114) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 --- applogic/chatglm.go | 6 +- applogic/tongyiqianwen.go | 50 ++--- config/config.go | 442 ++++++++++++++++++++++++++++++++------ 3 files changed, 405 insertions(+), 93 deletions(-) diff --git a/applogic/chatglm.go b/applogic/chatglm.go index 6373a6e..3405390 100644 --- a/applogic/chatglm.go +++ b/applogic/chatglm.go @@ -225,16 +225,16 @@ func (app *App) ChatHandlerGlm(w http.ResponseWriter, r *http.Request) { } // 获取配置信息 - apiKey := config.GetGlmApiKey() + apiKey := config.GetGlmApiKey(promptstr) // 创建请求体的映射结构 requestBody := map[string]interface{}{ - "model": config.GetGlmModel(), + "model": config.GetGlmModel(promptstr), "messages": messages, "do_sample": config.GetGlmDoSample(), "stream": config.GetuseSse(promptstr), "temperature": config.GetGlmTemperature(), "top_p": config.GetGlmTopP(), - "max_tokens": config.GetGlmMaxTokens(), + "max_tokens": config.GetGlmMaxTokens(promptstr), "stop": config.GetGlmStop(), //"tools": config.GetGlmTools(), 不太清楚参数格式 "tool_choice": config.GetGlmToolChoice(), diff --git a/applogic/tongyiqianwen.go b/applogic/tongyiqianwen.go index e067e90..e35e4d7 100644 --- a/applogic/tongyiqianwen.go +++ b/applogic/tongyiqianwen.go @@ -198,7 +198,7 @@ func (app *App) ChatHandlerTyqw(w http.ResponseWriter, r *http.Request) { fmtf.Printf("Tyqw上下文history:%v\n", history) // 构建请求到Tyqw API - apiURL := config.GetTyqwApiPath() + apiURL := config.GetTyqwApiPath(promptstr) // 构造消息历史和当前消息 messages := []map[string]interface{}{} @@ -216,29 +216,29 @@ func (app *App) ChatHandlerTyqw(w http.ResponseWriter, r *http.Request) { }) var isIncrementalOutput bool - if config.GetTyqwSseType() == 1 { + if config.GetTyqwSseType(promptstr) == 1 { isIncrementalOutput = true } // 获取配置信息 useSSE := config.GetuseSse(promptstr) - apiKey := config.GetTyqwKey() + apiKey := config.GetTyqwKey(promptstr) var requestBody map[string]interface{} if useSSE { // 构建请求体,根据提供的文档重新调整 requestBody = map[string]interface{}{ "parameters": map[string]interface{}{ - "max_tokens": config.GetTyqwMaxTokens(), // 最大生成的token数 - "temperature": config.GetTyqwTemperature(), // 控制随机性和多样性的温度 - "top_p": config.GetTyqwTopP(), // 核采样方法的概率阈值 - "top_k": config.GetTyqwTopK(), // 采样候选集的大小 - "repetition_penalty": config.GetTyqwRepetitionPenalty(), // 控制重复度的惩罚因子 - "stop": config.GetTyqwStopTokens(), // 停止标记 - "seed": config.GetTyqwSeed(), // 随机数种子 - "result_format": "message", // 返回结果的格式 - "enable_search": config.GetTyqwEnableSearch(), // 是否启用互联网搜索 - "incremental_output": isIncrementalOutput, // 是否使用增量SSE模式,使用增量模式会更快,rwkv和openai不支持增量模式 + "max_tokens": config.GetTyqwMaxTokens(promptstr), // 最大生成的token数 + "temperature": config.GetTyqwTemperature(promptstr), // 控制随机性和多样性的温度 + "top_p": config.GetTyqwTopP(promptstr), // 核采样方法的概率阈值 + "top_k": config.GetTyqwTopK(promptstr), // 采样候选集的大小 + "repetition_penalty": config.GetTyqwRepetitionPenalty(), // 控制重复度的惩罚因子 + "stop": config.GetTyqwStopTokens(), // 停止标记 + "seed": config.GetTyqwSeed(), // 随机数种子 + "result_format": "message", // 返回结果的格式 + "enable_search": config.GetTyqwEnableSearch(), // 是否启用互联网搜索 + "incremental_output": isIncrementalOutput, // 是否使用增量SSE模式,使用增量模式会更快,rwkv和openai不支持增量模式 }, - "model": config.GetTyqwModel(), // 指定对话模型 + "model": config.GetTyqwModel(promptstr), // 指定对话模型 "input": map[string]interface{}{ "messages": messages, // 用户与模型的对话历史 }, @@ -251,17 +251,17 @@ func (app *App) ChatHandlerTyqw(w http.ResponseWriter, r *http.Request) { // 构建请求体,根据提供的文档重新调整 requestBody = map[string]interface{}{ "parameters": map[string]interface{}{ - "max_tokens": config.GetTyqwMaxTokens(), // 最大生成的token数 - "temperature": config.GetTyqwTemperature(), // 控制随机性和多样性的温度 - "top_p": config.GetTyqwTopP(), // 核采样方法的概率阈值 - "top_k": config.GetTyqwTopK(), // 采样候选集的大小 - "repetition_penalty": config.GetTyqwRepetitionPenalty(), // 控制重复度的惩罚因子 - "stop": config.GetTyqwStopTokens(), // 停止标记 - "seed": config.GetTyqwSeed(), // 随机数种子 - "result_format": "message", // 返回结果的格式 - "enable_search": config.GetTyqwEnableSearch(), // 是否启用互联网搜索 + "max_tokens": config.GetTyqwMaxTokens(promptstr), // 最大生成的token数 + "temperature": config.GetTyqwTemperature(promptstr), // 控制随机性和多样性的温度 + "top_p": config.GetTyqwTopP(promptstr), // 核采样方法的概率阈值 + "top_k": config.GetTyqwTopK(promptstr), // 采样候选集的大小 + "repetition_penalty": config.GetTyqwRepetitionPenalty(), // 控制重复度的惩罚因子 + "stop": config.GetTyqwStopTokens(), // 停止标记 + "seed": config.GetTyqwSeed(), // 随机数种子 + "result_format": "message", // 返回结果的格式 + "enable_search": config.GetTyqwEnableSearch(), // 是否启用互联网搜索 }, - "model": config.GetTyqwModel(), // 指定对话模型 + "model": config.GetTyqwModel(promptstr), // 指定对话模型 "input": map[string]interface{}{ "messages": messages, // 用户与模型的对话历史 }, @@ -402,7 +402,7 @@ func (app *App) ChatHandlerTyqw(w http.ResponseWriter, r *http.Request) { reader := bufio.NewReader(resp.Body) var totalUsage structs.GPTUsageInfo - if config.GetTyqwSseType() == 1 { + if config.GetTyqwSseType(promptstr) == 1 { for { line, err := reader.ReadString('\n') if err != nil { diff --git a/config/config.go b/config/config.go index d3d2704..745d86a 100644 --- a/config/config.go +++ b/config/config.go @@ -2032,14 +2032,38 @@ func GetNo4Promptkeyboard() bool { return false } -// 获取TYQW API路径 -func GetTyqwApiPath() string { +// GetTyqwApiPath 获取TYQW API路径,可接受basename作为参数 +func GetTyqwApiPath(options ...string) string { mu.Lock() defer mu.Unlock() - if instance != nil { - return instance.Settings.TyqwApiPath + return getTyqwApiPathInternal(options...) +} + +// getTyqwApiPathInternal 内部逻辑执行函数,不处理锁,可以安全地递归调用 +func getTyqwApiPathInternal(options ...string) string { + // 检查是否有参数传递进来,以及是否为空字符串 + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.TyqwApiPath + } + return "" // 默认值或错误处理 } - return "" // 默认值或错误处理 + + // 使用传入的 basename + basename := options[0] + apiPathInterface, err := prompt.GetSettingFromFilename(basename, "TyqwApiPath") + if err != nil { + log.Println("Error retrieving TyqwApiPath:", err) + return getTyqwApiPathInternal() // 递归调用内部函数,不传递任何参数 + } + + apiPath, ok := apiPathInterface.(string) + if !ok { // 检查类型断言是否失败 + log.Println("Type assertion failed for TyqwApiPath, fetching default") + return getTyqwApiPathInternal() // 递归调用内部函数,不传递任何参数 + } + + return apiPath } // 获取TYQW最大Token数量,可接受basename作为参数 @@ -2076,44 +2100,140 @@ func getTyqwMaxTokensInternal(options ...string) int { return maxTokens } -// 获取TYQW温度设置 -func GetTyqwTemperature() float64 { +// GetTyqwTemperature 获取TYQW温度设置,可接受basename作为参数 +func GetTyqwTemperature(options ...string) float64 { mu.Lock() defer mu.Unlock() - if instance != nil { - return instance.Settings.TyqwTemperature + return getTyqwTemperatureInternal(options...) +} + +// getTyqwTemperatureInternal 内部逻辑执行函数,不处理锁,可以安全地递归调用 +func getTyqwTemperatureInternal(options ...string) float64 { + // 检查是否有参数传递进来,以及是否为空字符串 + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.TyqwTemperature + } + return 0.0 // 默认值或错误处理 } - return 0.0 // 默认值或错误处理 + + // 使用传入的 basename + basename := options[0] + temperatureInterface, err := prompt.GetSettingFromFilename(basename, "TyqwTemperature") + if err != nil { + log.Println("Error retrieving TyqwTemperature:", err) + return getTyqwTemperatureInternal() // 递归调用内部函数,不传递任何参数 + } + + temperature, ok := temperatureInterface.(float64) + if !ok { // 检查类型断言是否失败 + log.Println("Type assertion failed for TyqwTemperature, fetching default") + return getTyqwTemperatureInternal() // 递归调用内部函数,不传递任何参数 + } + + return temperature } -// 获取TYQW Top P -func GetTyqwTopP() float64 { +// GetTyqwTopP 获取TYQW Top P值,可接受basename作为参数 +func GetTyqwTopP(options ...string) float64 { mu.Lock() defer mu.Unlock() - if instance != nil { - return instance.Settings.TyqwTopP + return getTyqwTopPInternal(options...) +} + +// getTyqwTopPInternal 内部逻辑执行函数,不处理锁,可以安全地递归调用 +func getTyqwTopPInternal(options ...string) float64 { + // 检查是否有参数传递进来,以及是否为空字符串 + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.TyqwTopP + } + return 0.0 // 默认值或错误处理 } - return 0.0 // 默认值或错误处理 + + // 使用传入的 basename + basename := options[0] + topPInterface, err := prompt.GetSettingFromFilename(basename, "TyqwTopP") + if err != nil { + log.Println("Error retrieving TyqwTopP:", err) + return getTyqwTopPInternal() // 递归调用内部函数,不传递任何参数 + } + + topP, ok := topPInterface.(float64) + if !ok { // 检查类型断言是否失败 + log.Println("Type assertion failed for TyqwTopP, fetching default") + return getTyqwTopPInternal() // 递归调用内部函数,不传递任何参数 + } + + return topP } -// 获取TYQW Top K -func GetTyqwTopK() int { +// GetTyqwTopK 获取TYQW Top K设置,可接受basename作为参数 +func GetTyqwTopK(options ...string) int { mu.Lock() defer mu.Unlock() - if instance != nil { - return instance.Settings.TyqwTopK + return getTyqwTopKInternal(options...) +} + +// getTyqwTopKInternal 内部逻辑执行函数,不处理锁,可以安全地递归调用 +func getTyqwTopKInternal(options ...string) int { + // 检查是否有参数传递进来,以及是否为空字符串 + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.TyqwTopK + } + return 0 // 默认值或错误处理 } - return 0 // 默认值或错误处理 + + // 使用传入的 basename + basename := options[0] + topKInterface, err := prompt.GetSettingFromFilename(basename, "TyqwTopK") + if err != nil { + log.Println("Error retrieving TyqwTopK:", err) + return getTyqwTopKInternal() // 递归调用内部函数,不传递任何参数 + } + + topK, ok := topKInterface.(int) + if !ok { // 检查类型断言是否失败 + log.Println("Type assertion failed for TyqwTopK, fetching default") + return getTyqwTopKInternal() // 递归调用内部函数,不传递任何参数 + } + + return topK } -// 获取TYQW SSE类型 -func GetTyqwSseType() int { +// GetTyqwSseType 获取TYQW SSE类型,可接受basename作为参数 +func GetTyqwSseType(options ...string) int { mu.Lock() defer mu.Unlock() - if instance != nil { - return instance.Settings.TyqwSseType + return getTyqwSseTypeInternal(options...) +} + +// getTyqwSseTypeInternal 内部逻辑执行函数,不处理锁,可以安全地递归调用 +func getTyqwSseTypeInternal(options ...string) int { + // 检查是否有参数传递进来,以及是否为空字符串 + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.TyqwSseType + } + return 0 // 默认值或错误处理 } - return 0 // 默认值或错误处理 + + // 使用传入的 basename + basename := options[0] + sseTypeInterface, err := prompt.GetSettingFromFilename(basename, "TyqwSseType") + if err != nil { + log.Println("Error retrieving TyqwSseType:", err) + return getTyqwSseTypeInternal() // 递归调用内部函数,不传递任何参数 + } + + sseType, ok := sseTypeInterface.(int) + if !ok { // 检查类型断言是否失败 + log.Println("Type assertion failed for TyqwSseType, fetching default") + return getTyqwSseTypeInternal() // 递归调用内部函数,不传递任何参数 + } + + return sseType } // 获取TYQW用户名 @@ -2196,24 +2316,72 @@ func GetTyqwEnableSearch() bool { return false // 默认值或错误处理 } -// 获取TYQW模型名称 -func GetTyqwModel() string { +// GetTyqwModel 获取TYQW模型名称,可接受basename作为参数 +func GetTyqwModel(options ...string) string { mu.Lock() defer mu.Unlock() - if instance != nil { - return instance.Settings.TyqwModel + return getTyqwModelInternal(options...) +} + +// getTyqwModelInternal 内部逻辑执行函数,不处理锁,可以安全地递归调用 +func getTyqwModelInternal(options ...string) string { + // 检查是否有参数传递进来,以及是否为空字符串 + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.TyqwModel + } + return "default-model" // 默认值或错误处理 } - return "default-model" // 默认值或错误处理 + + // 使用传入的 basename + basename := options[0] + modelInterface, err := prompt.GetSettingFromFilename(basename, "TyqwModel") + if err != nil { + log.Println("Error retrieving TyqwModel:", err) + return getTyqwModelInternal() // 递归调用内部函数,不传递任何参数 + } + + model, ok := modelInterface.(string) + if !ok || model == "" { // 检查类型断言是否失败或结果为空字符串 + log.Println("Type assertion failed or empty string for TyqwModel, fetching default") + return getTyqwModelInternal() // 递归调用内部函数,不传递任何参数 + } + + return model } -// 获取TYQW API Key -func GetTyqwKey() string { +// GetTyqwKey 获取TYQW API Key,可接受basename作为参数 +func GetTyqwKey(options ...string) string { mu.Lock() defer mu.Unlock() - if instance != nil { - return instance.Settings.TyqwApiKey + return getTyqwKeyInternal(options...) +} + +// getTyqwKeyInternal 内部逻辑执行函数,不处理锁,可以安全地递归调用 +func getTyqwKeyInternal(options ...string) string { + // 检查是否有参数传递进来,以及是否为空字符串 + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.TyqwApiKey + } + return "" // 默认值或错误处理,表示没有找到有效的API Key + } + + // 使用传入的 basename + basename := options[0] + apiKeyInterface, err := prompt.GetSettingFromFilename(basename, "TyqwApiKey") + if err != nil { + log.Println("Error retrieving TyqwApiKey:", err) + return getTyqwKeyInternal() // 递归调用内部函数,不传递任何参数 + } + + apiKey, ok := apiKeyInterface.(string) + if !ok { // 检查类型断言是否失败 + log.Println("Type assertion failed for TyqwApiKey, fetching default") + return getTyqwKeyInternal() // 递归调用内部函数,不传递任何参数 } - return "" // 默认值或错误处理,表示没有找到有效的API Key + + return apiKey } // 获取TYQW Workspace @@ -2229,34 +2397,106 @@ func GetTyqworkspace() (string, error) { return "", fmt.Errorf("configuration instance is not initialized") // 错误处理,当配置实例未初始化时 } -// GetGlmApiPath 获取GLM API路径 -func GetGlmApiPath() string { +// GetGlmApiPath 获取GLM API路径,可接受basename作为参数 +func GetGlmApiPath(options ...string) string { mu.Lock() defer mu.Unlock() - if instance != nil { - return instance.Settings.GlmApiPath + return getGlmApiPathInternal(options...) +} + +// getGlmApiPathInternal 内部逻辑执行函数,不处理锁,可以安全地递归调用 +func getGlmApiPathInternal(options ...string) string { + // 检查是否有参数传递进来,以及是否为空字符串 + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.GlmApiPath + } + return "" // 默认值或错误处理 } - return "" // 默认值或错误处理 + + // 使用传入的 basename + basename := options[0] + apiPathInterface, err := prompt.GetSettingFromFilename(basename, "GlmApiPath") + if err != nil { + log.Println("Error retrieving GlmApiPath:", err) + return getGlmApiPathInternal() // 递归调用内部函数,不传递任何参数 + } + + apiPath, ok := apiPathInterface.(string) + if !ok { // 检查类型断言是否失败 + log.Println("Type assertion failed for GlmApiPath, fetching default") + return getGlmApiPathInternal() // 递归调用内部函数,不传递任何参数 + } + + return apiPath } -// GetGlmModel 获取模型编码 -func GetGlmModel() string { +// GetGlmModel 获取模型编码,可接受basename作为参数 +func GetGlmModel(options ...string) string { mu.Lock() defer mu.Unlock() - if instance != nil { - return instance.Settings.GlmModel + return getGlmModelInternal(options...) +} + +// getGlmModelInternal 内部逻辑执行函数,不处理锁,可以安全地递归调用 +func getGlmModelInternal(options ...string) string { + // 检查是否有参数传递进来,以及是否为空字符串 + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.GlmModel + } + return "" // 默认值或错误处理 } - return "" + + // 使用传入的 basename + basename := options[0] + modelInterface, err := prompt.GetSettingFromFilename(basename, "GlmModel") + if err != nil { + log.Println("Error retrieving GlmModel:", err) + return getGlmModelInternal() // 递归调用内部函数,不传递任何参数 + } + + model, ok := modelInterface.(string) + if !ok { // 检查类型断言是否失败 + log.Println("Type assertion failed for GlmModel, fetching default") + return getGlmModelInternal() // 递归调用内部函数,不传递任何参数 + } + + return model } -// GetGlmApiKey 获取模型编码 -func GetGlmApiKey() string { +// GetGlmApiKey 获取glm密钥,可接受basename作为参数 +func GetGlmApiKey(options ...string) string { mu.Lock() defer mu.Unlock() - if instance != nil { - return instance.Settings.GlmApiKey + return getGlmApiKeyInternal(options...) +} + +// getGlmApiKeyInternal 内部逻辑执行函数,不处理锁,可以安全地递归调用 +func getGlmApiKeyInternal(options ...string) string { + // 检查是否有参数传递进来,以及是否为空字符串 + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.GlmApiKey + } + return "" // 默认值或错误处理 } - return "" + + // 使用传入的 basename + basename := options[0] + apiKeyInterface, err := prompt.GetSettingFromFilename(basename, "GlmApiKey") + if err != nil { + log.Println("Error retrieving GlmApiKey:", err) + return getGlmApiKeyInternal() // 递归调用内部函数,不传递任何参数 + } + + apiKey, ok := apiKeyInterface.(string) + if !ok { // 检查类型断言是否失败 + log.Println("Type assertion failed for GlmApiKey, fetching default") + return getGlmApiKeyInternal() // 递归调用内部函数,不传递任何参数 + } + + return apiKey } // GetGlmMaxTokens 获取模型输出的最大tokens数,可接受basename作为参数 @@ -2293,14 +2533,38 @@ func getGlmMaxTokensInternal(options ...string) int { return maxTokens } -// GetGlmTemperature 获取模型的采样温度 -func GetGlmTemperature() float64 { +// GetGlmTemperature 获取模型的采样温度,可接受basename作为参数 +func GetGlmTemperature(options ...string) float64 { mu.Lock() defer mu.Unlock() - if instance != nil { - return instance.Settings.GlmTemperature + return getGlmTemperatureInternal(options...) +} + +// getGlmTemperatureInternal 内部逻辑执行函数,不处理锁,可以安全地递归调用 +func getGlmTemperatureInternal(options ...string) float64 { + // 检查是否有参数传递进来,以及是否为空字符串 + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.GlmTemperature + } + return 0.95 // 默认值或错误处理 + } + + // 使用传入的 basename + basename := options[0] + temperatureInterface, err := prompt.GetSettingFromFilename(basename, "GlmTemperature") + if err != nil { + log.Println("Error retrieving GlmTemperature:", err) + return getGlmTemperatureInternal() // 递归调用内部函数,不传递任何参数 + } + + temperature, ok := temperatureInterface.(float64) + if !ok { // 检查类型断言是否失败 + log.Println("Type assertion failed for GlmTemperature, fetching default") + return getGlmTemperatureInternal() // 递归调用内部函数,不传递任何参数 } - return 0.95 // 返回默认值 + + return temperature } // GetGlmDoSample 获取是否启用采样策略 @@ -2343,14 +2607,38 @@ func GetGlmRequestID() string { return "" // 返回默认值,表示没有设置 } -// GetGlmTopP 获取核取样概率 -func GetGlmTopP() float64 { +// GetGlmTopP 获取核取样概率,可接受basename作为参数 +func GetGlmTopP(options ...string) float64 { mu.Lock() defer mu.Unlock() - if instance != nil { - return instance.Settings.GlmTopP + return getGlmTopPInternal(options...) +} + +// getGlmTopPInternal 内部逻辑执行函数,不处理锁,可以安全地递归调用 +func getGlmTopPInternal(options ...string) float64 { + // 检查是否有参数传递进来,以及是否为空字符串 + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.GlmTopP + } + return 0.7 // 默认值或错误处理 + } + + // 使用传入的 basename + basename := options[0] + topPInterface, err := prompt.GetSettingFromFilename(basename, "GlmTopP") + if err != nil { + log.Println("Error retrieving GlmTopP:", err) + return getGlmTopPInternal() // 递归调用内部函数,不传递任何参数 + } + + topP, ok := topPInterface.(float64) + if !ok { // 检查类型断言是否失败 + log.Println("Type assertion failed for GlmTopP, fetching default") + return getGlmTopPInternal() // 递归调用内部函数,不传递任何参数 } - return 0.7 // 返回默认值 + + return topP } // GetGlmStop 获取停止生成的词列表 @@ -2373,12 +2661,36 @@ func GetGlmTools() []string { return []string{} // 返回空切片,表示没有工具设置 } -// 获取GroupHintWords列表 -func GetGroupHintWords() []string { +// GetGroupHintWords 获取GroupHintWords列表,可接受basename作为参数 +func GetGroupHintWords(options ...string) []string { mu.Lock() defer mu.Unlock() - if instance != nil { - return instance.Settings.GroupHintWords + return getGroupHintWordsInternal(options...) +} + +// getGroupHintWordsInternal 内部逻辑执行函数,不处理锁,可以安全地递归调用 +func getGroupHintWordsInternal(options ...string) []string { + // 检查是否有参数传递进来,以及是否为空字符串 + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.GroupHintWords + } + return nil // 默认值或错误处理 } - return nil + + // 使用传入的 basename + basename := options[0] + hintWordsInterface, err := prompt.GetSettingFromFilename(basename, "GroupHintWords") + if err != nil { + log.Println("Error retrieving GroupHintWords:", err) + return getGroupHintWordsInternal() // 递归调用内部函数,不传递任何参数 + } + + hintWords, ok := hintWordsInterface.([]string) + if !ok { // 检查类型断言是否失败 + log.Println("Type assertion failed for GroupHintWords, fetching default") + return getGroupHintWordsInternal() // 递归调用内部函数,不传递任何参数 + } + + return hintWords } From 4b1aa8dde3f827618f8f67d27250b0228876574d Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Tue, 14 May 2024 20:22:07 +0800 Subject: [PATCH 03/30] Beta117 (#115) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 * beta117 --- applogic/gensokyo.go | 2 +- utils/blacklist.go | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/applogic/gensokyo.go b/applogic/gensokyo.go index 71369e8..60d5a94 100644 --- a/applogic/gensokyo.go +++ b/applogic/gensokyo.go @@ -264,7 +264,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } if utils.BlacklistIntercept(message, selfid) { - fmtf.Printf("userid:[%v]这位用户在黑名单中,被拦截", message.UserID) + fmtf.Printf("userid:[%v]groupid:[%v]这位用户或群在黑名单中,被拦截", message.UserID, message.GroupID) return } diff --git a/utils/blacklist.go b/utils/blacklist.go index 6d3b1f2..77b1aea 100644 --- a/utils/blacklist.go +++ b/utils/blacklist.go @@ -94,6 +94,26 @@ func WatchBlacklist(filePath string) { // BlacklistIntercept 检查用户ID是否在黑名单中,如果在,则发送预设消息 func BlacklistIntercept(message structs.OnebotGroupMessage, selfid string) bool { + // 检查群ID是否在黑名单中 + if IsInBlacklist(strconv.FormatInt(message.GroupID, 10)) { + // 获取黑名单响应消息 + responseMessage := config.GetBlacklistResponseMessages() + + // 根据消息类型发送响应 + if message.RealMessageType == "group_private" || message.MessageType == "private" { + if !config.GetUsePrivateSSE() { + SendPrivateMessage(message.UserID, responseMessage, selfid) + } else { + SendSSEPrivateMessage(message.UserID, responseMessage) + } + } else { + SendGroupMessage(message.GroupID, message.UserID, responseMessage, selfid) + } + + fmt.Printf("groupid:[%v]这个群在黑名单中,被拦截\n", message.GroupID) + return true // 拦截 + } + // 检查用户ID是否在黑名单中 if IsInBlacklist(strconv.FormatInt(message.UserID, 10)) { // 获取黑名单响应消息 @@ -113,5 +133,6 @@ func BlacklistIntercept(message structs.OnebotGroupMessage, selfid string) bool fmt.Printf("userid:[%v]这位用户在黑名单中,被拦截\n", message.UserID) return true // 拦截 } + return false // 用户ID不在黑名单中,不拦截 } From ec731c2f098623884ba4452bc0b7d3b81b7e76aa Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Thu, 16 May 2024 19:05:06 +0800 Subject: [PATCH 04/30] Beta118 (#116) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 * beta117 * beta118 --- applogic/chatglm.go | 41 +++++++++++++++++-------------- applogic/ernie.go | 15 ++++++++---- applogic/tongyiqianwen.go | 49 ++++++++++++++++++++++--------------- config/config.go | 44 +++++++++++++++++++++++++++++++++ prompt/prompt.go | 23 +++++++++++++++++ template/config_template.go | 2 +- 6 files changed, 130 insertions(+), 44 deletions(-) diff --git a/applogic/chatglm.go b/applogic/chatglm.go index 3405390..14b4aac 100644 --- a/applogic/chatglm.go +++ b/applogic/chatglm.go @@ -133,7 +133,7 @@ func (app *App) ChatHandlerGlm(w http.ResponseWriter, r *http.Request) { } } else { // 只获取系统提示词 - systemMessage, err := prompt.FindFirstSystemMessage(history) + systemMessage, err := prompt.GetFirstSystemMessageStruct(promptstr) if err != nil { fmt.Println("Error:", err) } else { @@ -164,28 +164,33 @@ func (app *App) ChatHandlerGlm(w http.ResponseWriter, r *http.Request) { fmtf.Printf("Error getting system history: %v,promptstr[%v]\n", err, promptstr) return } - - // 处理增强QA逻辑 if config.GetEnhancedQA(promptstr) { - // 确保系统历史与用户或助手历史数量一致,如果不足,则补足空的历史记录 - // 因为最后一个成员让给当前QA,所以-1 - if len(systemHistory)-2 > len(userHistory) { - difference := len(systemHistory) - len(userHistory) + systemHistory, err := prompt.GetMessagesExcludingSystem(promptstr) + if err != nil { + fmt.Printf("Error getting system history: %v\n", err) + return + } + + // 计算需要补足的历史记录数量 + neededHistoryCount := len(systemHistory) - 2 // 最后两条留给当前QA处理 + if neededHistoryCount > len(userHistory) { + // 补足用户或助手历史 + difference := neededHistoryCount - len(userHistory) for i := 0; i < difference; i++ { - userHistory = append(userHistory, structs.Message{Text: "", Role: "user"}) - userHistory = append(userHistory, structs.Message{Text: "", Role: "assistant"}) + if i%2 != 0 { + userHistory = append(userHistory, structs.Message{Text: "", Role: "user"}) + } else { + userHistory = append(userHistory, structs.Message{Text: "", Role: "assistant"}) + } } } - // 如果系统历史中只有一个成员,跳过覆盖逻辑,留给后续处理 - if len(systemHistory) > 1 { - // 将系统历史(除最后2个成员外)附加到相应的用户或助手历史上,采用倒序方式处理最近的记录 - for i := 0; i < len(systemHistory)-2; i++ { - sysMsg := systemHistory[i] - index := len(userHistory) - len(systemHistory) + i - if index >= 0 && index < len(userHistory) && (userHistory[index].Role == "user" || userHistory[index].Role == "assistant") { - userHistory[index].Text += fmt.Sprintf(" (%s)", sysMsg.Text) - } + // 附加系统历史到用户或助手历史,除了最后两条 + for i := 0; i < len(systemHistory)-2; i++ { + sysMsg := systemHistory[i] + index := len(userHistory) - neededHistoryCount + i + if index >= 0 && index < len(userHistory) { + userHistory[index].Text += fmt.Sprintf(" (%s)", sysMsg.Text) } } } else { diff --git a/applogic/ernie.go b/applogic/ernie.go index 4dbe725..eadb9d8 100644 --- a/applogic/ernie.go +++ b/applogic/ernie.go @@ -133,12 +133,17 @@ func (app *App) ChatHandlerErnie(w http.ResponseWriter, r *http.Request) { // 处理增强QA逻辑 if config.GetEnhancedQA(promptstr) { // 确保系统历史与用户或助手历史数量一致,如果不足,则补足空的历史记录 - // 因为最后一个成员让给当前QA,所以-1 - if len(systemHistory)-2 > len(userHistory) { - difference := len(systemHistory) - len(userHistory) + // 计算需要补足的历史记录数量 + neededHistoryCount := len(systemHistory) - 2 // 最后两条留给当前QA处理 + if neededHistoryCount > len(userHistory) { + // 补足用户或助手历史 + difference := neededHistoryCount - len(userHistory) for i := 0; i < difference; i++ { - userHistory = append(userHistory, structs.Message{Text: "", Role: "user"}) - userHistory = append(userHistory, structs.Message{Text: "", Role: "assistant"}) + if i%2 != 0 { + userHistory = append(userHistory, structs.Message{Text: "", Role: "user"}) + } else { + userHistory = append(userHistory, structs.Message{Text: "", Role: "assistant"}) + } } } diff --git a/applogic/tongyiqianwen.go b/applogic/tongyiqianwen.go index e35e4d7..8277437 100644 --- a/applogic/tongyiqianwen.go +++ b/applogic/tongyiqianwen.go @@ -128,7 +128,7 @@ func (app *App) ChatHandlerTyqw(w http.ResponseWriter, r *http.Request) { } } else { // 只获取系统提示词 - systemMessage, err := prompt.FindFirstSystemMessage(history) + systemMessage, err := prompt.GetFirstSystemMessageStruct(promptstr) if err != nil { fmt.Println("Error:", err) } else { @@ -138,7 +138,7 @@ func (app *App) ChatHandlerTyqw(w http.ResponseWriter, r *http.Request) { } } } - + // TODO: msgid是空的开始第一句也要处理 插入 // 获取历史信息 if msg.ParentMessageID != "" { userhistory, err := app.getHistory(msg.ConversationID, msg.ParentMessageID) @@ -159,28 +159,33 @@ func (app *App) ChatHandlerTyqw(w http.ResponseWriter, r *http.Request) { fmtf.Printf("Error getting system history: %v,promptstr[%v]\n", err, promptstr) return } - - // 处理增强QA逻辑 if config.GetEnhancedQA(promptstr) { - // 确保系统历史与用户或助手历史数量一致,如果不足,则补足空的历史记录 - // 因为最后一个成员让给当前QA,所以-1 - if len(systemHistory)-2 > len(userHistory) { - difference := len(systemHistory) - len(userHistory) + systemHistory, err := prompt.GetMessagesExcludingSystem(promptstr) + if err != nil { + fmt.Printf("Error getting system history: %v\n", err) + return + } + + // 计算需要补足的历史记录数量 + neededHistoryCount := len(systemHistory) - 2 // 最后两条留给当前QA处理 + if neededHistoryCount > len(userHistory) { + // 补足用户或助手历史 + difference := neededHistoryCount - len(userHistory) for i := 0; i < difference; i++ { - userHistory = append(userHistory, structs.Message{Text: "", Role: "user"}) - userHistory = append(userHistory, structs.Message{Text: "", Role: "assistant"}) + if i%2 != 0 { + userHistory = append(userHistory, structs.Message{Text: "", Role: "user"}) + } else { + userHistory = append(userHistory, structs.Message{Text: "", Role: "assistant"}) + } } } - // 如果系统历史中只有一个成员,跳过覆盖逻辑,留给后续处理 - if len(systemHistory) > 1 { - // 将系统历史(除最后2个成员外)附加到相应的用户或助手历史上,采用倒序方式处理最近的记录 - for i := 0; i < len(systemHistory)-2; i++ { - sysMsg := systemHistory[i] - index := len(userHistory) - len(systemHistory) + i - if index >= 0 && index < len(userHistory) && (userHistory[index].Role == "user" || userHistory[index].Role == "assistant") { - userHistory[index].Text += fmt.Sprintf(" (%s)", sysMsg.Text) - } + // 附加系统历史到用户或助手历史,除了最后两条 + for i := 0; i < len(systemHistory)-2; i++ { + sysMsg := systemHistory[i] + index := len(userHistory) - neededHistoryCount + i + if index >= 0 && index < len(userHistory) { + userHistory[index].Text += fmt.Sprintf(" (%s)", sysMsg.Text) } } } else { @@ -199,6 +204,7 @@ func (app *App) ChatHandlerTyqw(w http.ResponseWriter, r *http.Request) { // 构建请求到Tyqw API apiURL := config.GetTyqwApiPath(promptstr) + fmtf.Printf("Tyqw请求地址:%v\n", apiURL) // 构造消息历史和当前消息 messages := []map[string]interface{}{} @@ -216,8 +222,11 @@ func (app *App) ChatHandlerTyqw(w http.ResponseWriter, r *http.Request) { }) var isIncrementalOutput bool - if config.GetTyqwSseType(promptstr) == 1 { + tyqwssetype := config.GetTyqwSseType(promptstr) + if tyqwssetype == 1 { isIncrementalOutput = true + } else if tyqwssetype == 2 { + isIncrementalOutput = false } // 获取配置信息 useSSE := config.GetuseSse(promptstr) diff --git a/config/config.go b/config/config.go index 745d86a..a7368bc 100644 --- a/config/config.go +++ b/config/config.go @@ -262,6 +262,10 @@ func getMaxTokensHunyuanInternal(options ...string) int { return getMaxTokensHunyuanInternal() // 递归调用内部函数,不传递任何参数 } + if maxTokensHunyuan == 0 { + return getMaxTokensHunyuanInternal() + } + return maxTokensHunyuan } @@ -316,6 +320,10 @@ func getWenxinApiPathInternal(options ...string) string { return getWenxinApiPathInternal() // 递归调用内部函数,不传递任何参数 } + if apiPath == "" { + return getWenxinApiPathInternal() + } + return apiPath } @@ -360,6 +368,10 @@ func getGptModelInternal(options ...string) string { return getGptModelInternal() // 递归调用内部函数,不传递任何参数 } + if gptModel == "" { + return getGptModelInternal() + } + return gptModel } @@ -2063,6 +2075,10 @@ func getTyqwApiPathInternal(options ...string) string { return getTyqwApiPathInternal() // 递归调用内部函数,不传递任何参数 } + if apiPath == "" { + return getTyqwApiPathInternal() + } + return apiPath } @@ -2097,6 +2113,10 @@ func getTyqwMaxTokensInternal(options ...string) int { return getTyqwMaxTokensInternal() // 递归调用内部函数,不传递任何参数 } + if maxTokens == 0 { + return getTyqwMaxTokensInternal() + } + return maxTokens } @@ -2131,6 +2151,10 @@ func getTyqwTemperatureInternal(options ...string) float64 { return getTyqwTemperatureInternal() // 递归调用内部函数,不传递任何参数 } + if temperature == 0 { + return getTyqwTemperatureInternal() + } + return temperature } @@ -2165,6 +2189,10 @@ func getTyqwTopPInternal(options ...string) float64 { return getTyqwTopPInternal() // 递归调用内部函数,不传递任何参数 } + if topP == 0 { + return getTyqwTopPInternal() + } + return topP } @@ -2199,6 +2227,10 @@ func getTyqwTopKInternal(options ...string) int { return getTyqwTopKInternal() // 递归调用内部函数,不传递任何参数 } + if topK == 0 { + return getTyqwTopKInternal() + } + return topK } @@ -2233,6 +2265,10 @@ func getTyqwSseTypeInternal(options ...string) int { return getTyqwSseTypeInternal() // 递归调用内部函数,不传递任何参数 } + if sseType == 0 { + return getTyqwSseTypeInternal() + } + return sseType } @@ -2347,6 +2383,10 @@ func getTyqwModelInternal(options ...string) string { return getTyqwModelInternal() // 递归调用内部函数,不传递任何参数 } + if model == "" { + return getTyqwModelInternal() + } + return model } @@ -2381,6 +2421,10 @@ func getTyqwKeyInternal(options ...string) string { return getTyqwKeyInternal() // 递归调用内部函数,不传递任何参数 } + if apiKey == "" { + return getTyqwKeyInternal() + } + return apiKey } diff --git a/prompt/prompt.go b/prompt/prompt.go index ba3e8f0..1892f24 100644 --- a/prompt/prompt.go +++ b/prompt/prompt.go @@ -192,6 +192,29 @@ func FindFirstSystemMessage(history []structs.Message) (structs.Message, error) return structs.Message{}, fmt.Errorf("no system message found in history") } +// GetFirstSystemMessage returns the first message that is of "system" role. +func GetFirstSystemMessageStruct(basename string) (structs.Message, error) { + lock.RLock() + defer lock.RUnlock() + + filename := basename + ".yml" + promptFile, exists := promptsCache[filename] + if !exists { + return structs.Message{}, fmt.Errorf("no data for file: %s", filename) + } + + for _, prompt := range promptFile.Prompts { + if prompt.Role == "system" || prompt.Role == "System" { + return structs.Message{ + Text: prompt.Content, + Role: prompt.Role, + }, nil + } + } + + return structs.Message{}, fmt.Errorf("no system message found in file: %s", filename) +} + // 返回除了 "system" 角色之外的所有消息 // GetMessagesExcludingSystem returns a list of messages that are not of "system" role, // randomly selecting from options separated by "||" in prompt contents. diff --git a/template/config_template.go b/template/config_template.go index f6913f1..4c559a8 100644 --- a/template/config_template.go +++ b/template/config_template.go @@ -155,7 +155,7 @@ settings: tyqwRepetitionPenalty : 1.1 # 用于控制模型生成时的重复度。提高repetition_penalty时可以降低模型生成的重复度。1.0表示不做惩罚,默认为1.1。没有严格的取值范围。 tyqwTopK: 40 # 从概率最高的K个令牌中采样 tyqwSeed : 1234 # 生成时使用的随机数种子,用户控制模型生成内容的随机性。seed支持无符号64位整数,默认值为1234。在使用seed时,模型将尽可能生成相同或相似的结果,但目前不保证每次生成的结果完全相同。 - tyqwSseType: 1 # 同gptSseType, 例如0代表不使用, 1代表使用 + tyqwSseType: 1 # 1=默认,sse发新内容 2=sse内容递增(不推荐) tyqwGlobalPenalty: false # 是否在全局上应用频率惩罚 tyqwStop: # 停止生成的标记列表 - "\n\nUser" From 4a09fba361a13944fdfb0e5a257e0f0331aac49d Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Fri, 17 May 2024 17:12:45 +0800 Subject: [PATCH 05/30] Beta119 (#117) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 * beta117 * beta118 * beta119 --- applogic/hunyuan.go | 266 ++++++++++++- applogic/rwkv.go | 39 +- config/config.go | 100 +++++ hunyuan/client.go | 256 +++++++++++-- hunyuan/errors.go | 6 +- hunyuan/models.go | 719 +++++++++++++++++++++++++++++++----- structs/struct.go | 7 +- template/config_template.go | 5 +- utils/utils.go | 11 + 9 files changed, 1254 insertions(+), 155 deletions(-) diff --git a/applogic/hunyuan.go b/applogic/hunyuan.go index 8168e87..2efd13a 100644 --- a/applogic/hunyuan.go +++ b/applogic/hunyuan.go @@ -114,9 +114,22 @@ func (app *App) ChatHandlerHunyuan(w http.ResponseWriter, r *http.Request) { } } } else { - history, err = prompt.GetMessagesFromFilename(promptstr) - if err != nil { - fmtf.Printf("prompt.GetMessagesFromFilename error: %v\n", err) + // 默认执行 正常提示词顺序 + if !config.GetEnhancedQA(promptstr) { + history, err = prompt.GetMessagesFromFilename(promptstr) + if err != nil { + fmtf.Printf("prompt.GetMessagesFromFilename error: %v\n", err) + } + } else { + // 只获取系统提示词 + systemMessage, err := prompt.GetFirstSystemMessageStruct(promptstr) + if err != nil { + fmt.Println("Error:", err) + } else { + // 如果找到system消息,将其添加到历史数组中 + history = append(history, systemMessage) + fmt.Println("Added system message back to history.") + } } } @@ -137,31 +150,38 @@ func (app *App) ChatHandlerHunyuan(w http.ResponseWriter, r *http.Request) { // 获取系统级预埋的系统自定义QA对 systemHistory, err := prompt.GetMessagesExcludingSystem(promptstr) if err != nil { - fmtf.Printf("Error getting system history: %v\n", err) + fmtf.Printf("Error getting system history: %v,promptstr[%v]\n", err, promptstr) return } // 处理增强QA逻辑 if config.GetEnhancedQA(promptstr) { - // 确保系统历史与用户或助手历史数量一致,如果不足,则补足空的历史记录 - // 因为最后一个成员让给当前QA,所以-1 - if len(systemHistory)-2 > len(userHistory) { - difference := len(systemHistory) - len(userHistory) + systemHistory, err := prompt.GetMessagesExcludingSystem(promptstr) + if err != nil { + fmt.Printf("Error getting system history: %v\n", err) + return + } + + // 计算需要补足的历史记录数量 + neededHistoryCount := len(systemHistory) - 2 // 最后两条留给当前QA处理 + if neededHistoryCount > len(userHistory) { + // 补足用户或助手历史 + difference := neededHistoryCount - len(userHistory) for i := 0; i < difference; i++ { - userHistory = append(userHistory, structs.Message{Text: "", Role: "user"}) - userHistory = append(userHistory, structs.Message{Text: "", Role: "assistant"}) + if i%2 != 0 { + userHistory = append(userHistory, structs.Message{Text: "", Role: "user"}) + } else { + userHistory = append(userHistory, structs.Message{Text: "", Role: "assistant"}) + } } } - // 如果系统历史中只有一个成员,跳过覆盖逻辑,留给后续处理 - if len(systemHistory) > 1 { - // 将系统历史(除最后2个成员外)附加到相应的用户或助手历史上,采用倒序方式处理最近的记录 - for i := 0; i < len(systemHistory)-2; i++ { - sysMsg := systemHistory[i] - index := len(userHistory) - len(systemHistory) + i - if index >= 0 && index < len(userHistory) && (userHistory[index].Role == "user" || userHistory[index].Role == "assistant") { - userHistory[index].Text += fmt.Sprintf(" (%s)", sysMsg.Text) - } + // 附加系统历史到用户或助手历史,除了最后两条 + for i := 0; i < len(systemHistory)-2; i++ { + sysMsg := systemHistory[i] + index := len(userHistory) - neededHistoryCount + i + if index >= 0 && index < len(userHistory) { + userHistory[index].Text += fmt.Sprintf(" (%s)", sysMsg.Text) } } } else { @@ -181,6 +201,16 @@ func (app *App) ChatHandlerHunyuan(w http.ResponseWriter, r *http.Request) { if config.GetHunyuanType() == 0 { // 构建 hunyuan 请求 request := hunyuan.NewChatProRequest() + // 配置块 + request.StreamModeration = new(bool) + *request.StreamModeration = config.GetHunyuanStreamModeration(promptstr) + request.Stream = new(bool) + *request.Stream = config.GetHunyuanStreamModeration(promptstr) + request.TopP = new(float64) + *request.TopP = config.GetTopPHunyuan(promptstr) + request.Temperature = new(float64) + *request.Temperature = config.GetTemperatureHunyuan(promptstr) + // 添加历史信息 for _, hMsg := range history { content := hMsg.Text // 创建新变量 @@ -335,9 +365,20 @@ func (app *App) ChatHandlerHunyuan(w http.ResponseWriter, r *http.Request) { fmtf.Fprintf(w, "data: %s\n\n", string(finalResponseJSON)) flusher.Flush() } - } else { + } else if config.GetHunyuanType() == 1 { // 构建 hunyuan 标准版请求 request := hunyuan.NewChatStdRequest() + + // 配置块 + request.StreamModeration = new(bool) + *request.StreamModeration = config.GetHunyuanStreamModeration(promptstr) + request.Stream = new(bool) + *request.Stream = config.GetHunyuanStreamModeration(promptstr) + request.TopP = new(float64) + *request.TopP = config.GetTopPHunyuan(promptstr) + request.Temperature = new(float64) + *request.Temperature = config.GetTemperatureHunyuan(promptstr) + // 添加历史信息 for _, hMsg := range history { content := hMsg.Text // 创建新变量 @@ -493,6 +534,191 @@ func (app *App) ChatHandlerHunyuan(w http.ResponseWriter, r *http.Request) { flusher.Flush() } + } else if config.GetHunyuanType() == 2 || config.GetHunyuanType() == 3 || config.GetHunyuanType() == 4 || config.GetHunyuanType() == 5 { + // 构建 hunyuan 请求 + request := hunyuan.NewChatCompletionsRequest() + // 添加历史信息 + for _, hMsg := range history { + content := hMsg.Text // 创建新变量 + role := hMsg.Role // 创建新变量 + hunyuanMsg := hunyuan.Message{ + Content: &content, // 引用新变量的地址 + Role: &role, // 引用新变量的地址 + } + request.Messages = append(request.Messages, &hunyuanMsg) + } + + // 添加当前用户消息 + currentUserContent := msg.Text // 创建新变量 + currentUserRole := msg.Role // 创建新变量 + currentUserMsg := hunyuan.Message{ + Content: ¤tUserContent, // 引用新变量的地址 + Role: ¤tUserRole, // 引用新变量的地址 + } + request.Messages = append(request.Messages, ¤tUserMsg) + + // 获取HunyuanType并设置对应的Model + switch config.GetHunyuanType() { + case 2: + request.Model = new(string) + *request.Model = "hunyuan-lite" + case 3: + request.Model = new(string) + *request.Model = "hunyuan-standard" + case 4: + request.Model = new(string) + *request.Model = "hunyuan-standard-256K" + case 5: + request.Model = new(string) + *request.Model = "hunyuan-pro" + default: + request.Model = new(string) + *request.Model = "default-value" + } + fmtf.Printf("请求的混元模型类型:%v", *request.Model) + request.StreamModeration = new(bool) + *request.StreamModeration = config.GetHunyuanStreamModeration(promptstr) + request.Stream = new(bool) + *request.Stream = config.GetHunyuanStreamModeration(promptstr) + request.TopP = new(float64) + *request.TopP = config.GetTopPHunyuan(promptstr) + request.Temperature = new(float64) + *request.Temperature = config.GetTemperatureHunyuan(promptstr) + + // 打印请求以进行调试 + utils.PrintChatCompletionsRequest(request) + + // 发送请求并获取响应 + response, err := app.Client.ChatCompletions(request) + if err != nil { + http.Error(w, fmtf.Sprintf("hunyuanapi返回错误: %v", err), http.StatusInternalServerError) + return + } + if !config.GetuseSse(promptstr) { + // 解析响应 + var responseTextBuilder strings.Builder + var totalUsage structs.UsageInfo + for event := range response.BaseSSEResponse.Events { + if event.Err != nil { + http.Error(w, fmtf.Sprintf("接收事件时发生错误: %v", event.Err), http.StatusInternalServerError) + return + } + + // 解析事件数据 + var eventData map[string]interface{} + if err := json.Unmarshal(event.Data, &eventData); err != nil { + http.Error(w, fmtf.Sprintf("解析事件数据出错: %v", err), http.StatusInternalServerError) + return + } + + // 使用extractEventDetails函数提取信息 + responseText, usageInfo := utils.ExtractEventDetails(eventData) + responseTextBuilder.WriteString(responseText) + totalUsage.PromptTokens += usageInfo.PromptTokens + totalUsage.CompletionTokens += usageInfo.CompletionTokens + } + // 现在responseTextBuilder中的内容是所有AI助手回复的组合 + responseText := responseTextBuilder.String() + + assistantMessageID, err := app.addMessage(structs.Message{ + ConversationID: msg.ConversationID, + ParentMessageID: userMessageID, + Text: responseText, + Role: "assistant", + }) + + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // 构造响应 + responseMap := map[string]interface{}{ + "response": responseText, + "conversationId": msg.ConversationID, + "messageId": assistantMessageID, + "details": map[string]interface{}{ + "usage": totalUsage, + }, + } + + json.NewEncoder(w).Encode(responseMap) + } else { + // 设置SSE相关的响应头部 + w.Header().Set("Content-Type", "text/event-stream") + w.Header().Set("Cache-Control", "no-cache") + w.Header().Set("Connection", "keep-alive") + + flusher, ok := w.(http.Flusher) + if !ok { + http.Error(w, "Streaming unsupported!", http.StatusInternalServerError) + return + } + + var responseTextBuilder strings.Builder + var totalUsage structs.UsageInfo + + for event := range response.BaseSSEResponse.Events { + if event.Err != nil { + fmtf.Fprintf(w, "data: %s\n\n", fmtf.Sprintf("接收事件时发生错误: %v", event.Err)) + flusher.Flush() + continue + } + + // 解析事件数据和提取信息 + var eventData map[string]interface{} + if err := json.Unmarshal(event.Data, &eventData); err != nil { + fmtf.Fprintf(w, "data: %s\n\n", fmtf.Sprintf("解析事件数据出错: %v", err)) + flusher.Flush() + continue + } + + responseText, usageInfo := utils.ExtractEventDetails(eventData) + responseTextBuilder.WriteString(responseText) + totalUsage.PromptTokens += usageInfo.PromptTokens + totalUsage.CompletionTokens += usageInfo.CompletionTokens + + // 发送当前事件的响应数据,但不包含assistantMessageID + //fmtf.Printf("发送当前事件的响应数据,但不包含assistantMessageID\n") + tempResponseMap := map[string]interface{}{ + "response": responseText, + "conversationId": msg.ConversationID, + "details": map[string]interface{}{ + "usage": usageInfo, + }, + } + tempResponseJSON, _ := json.Marshal(tempResponseMap) + fmtf.Fprintf(w, "data: %s\n\n", string(tempResponseJSON)) + flusher.Flush() + } + + // 处理完所有事件后,生成并发送包含assistantMessageID的最终响应 + responseText := responseTextBuilder.String() + fmtf.Printf("处理完所有事件后,生成并发送包含assistantMessageID的最终响应:%v\n", responseText) + assistantMessageID, err := app.addMessage(structs.Message{ + ConversationID: msg.ConversationID, + ParentMessageID: userMessageID, + Text: responseText, + Role: "assistant", + }) + + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + finalResponseMap := map[string]interface{}{ + "response": responseText, + "conversationId": msg.ConversationID, + "messageId": assistantMessageID, + "details": map[string]interface{}{ + "usage": totalUsage, + }, + } + finalResponseJSON, _ := json.Marshal(finalResponseMap) + fmtf.Fprintf(w, "data: %s\n\n", string(finalResponseJSON)) + flusher.Flush() + } } } diff --git a/applogic/rwkv.go b/applogic/rwkv.go index acc3af3..543fd90 100644 --- a/applogic/rwkv.go +++ b/applogic/rwkv.go @@ -128,7 +128,7 @@ func (app *App) ChatHandlerRwkv(w http.ResponseWriter, r *http.Request) { } } else { // 只获取系统提示词 - systemMessage, err := prompt.FindFirstSystemMessage(history) + systemMessage, err := prompt.GetFirstSystemMessageStruct(promptstr) if err != nil { fmt.Println("Error:", err) } else { @@ -162,25 +162,32 @@ func (app *App) ChatHandlerRwkv(w http.ResponseWriter, r *http.Request) { // 处理增强QA逻辑 if config.GetEnhancedQA(promptstr) { - // 确保系统历史与用户或助手历史数量一致,如果不足,则补足空的历史记录 - // 因为最后一个成员让给当前QA,所以-1 - if len(systemHistory)-2 > len(userHistory) { - difference := len(systemHistory) - len(userHistory) + systemHistory, err := prompt.GetMessagesExcludingSystem(promptstr) + if err != nil { + fmt.Printf("Error getting system history: %v\n", err) + return + } + + // 计算需要补足的历史记录数量 + neededHistoryCount := len(systemHistory) - 2 // 最后两条留给当前QA处理 + if neededHistoryCount > len(userHistory) { + // 补足用户或助手历史 + difference := neededHistoryCount - len(userHistory) for i := 0; i < difference; i++ { - userHistory = append(userHistory, structs.Message{Text: "", Role: "user"}) - userHistory = append(userHistory, structs.Message{Text: "", Role: "assistant"}) + if i%2 != 0 { + userHistory = append(userHistory, structs.Message{Text: "", Role: "user"}) + } else { + userHistory = append(userHistory, structs.Message{Text: "", Role: "assistant"}) + } } } - // 如果系统历史中只有一个成员,跳过覆盖逻辑,留给后续处理 - if len(systemHistory) > 1 { - // 将系统历史(除最后2个成员外)附加到相应的用户或助手历史上,采用倒序方式处理最近的记录 - for i := 0; i < len(systemHistory)-2; i++ { - sysMsg := systemHistory[i] - index := len(userHistory) - len(systemHistory) + i - if index >= 0 && index < len(userHistory) && (userHistory[index].Role == "user" || userHistory[index].Role == "assistant") { - userHistory[index].Text += fmt.Sprintf(" (%s)", sysMsg.Text) - } + // 附加系统历史到用户或助手历史,除了最后两条 + for i := 0; i < len(systemHistory)-2; i++ { + sysMsg := systemHistory[i] + index := len(userHistory) - neededHistoryCount + i + if index >= 0 && index < len(userHistory) { + userHistory[index].Text += fmt.Sprintf(" (%s)", sysMsg.Text) } } } else { diff --git a/config/config.go b/config/config.go index a7368bc..c87ae21 100644 --- a/config/config.go +++ b/config/config.go @@ -1229,6 +1229,10 @@ func getRwkvMaxTokensInternal(options ...string) int { return getRwkvMaxTokensInternal() // 递归调用内部函数,不传递任何参数 } + if maxTokens == 0 { + return getRwkvMaxTokensInternal() // 递归调用内部函数,不传递任何参数 + } + return maxTokens } @@ -2738,3 +2742,99 @@ func getGroupHintWordsInternal(options ...string) []string { return hintWords } + +// 获取HunyuanStreamModeration值 +func GetHunyuanStreamModeration(options ...string) bool { + mu.Lock() + defer mu.Unlock() + return getHunyuanStreamModerationInternal(options...) +} + +// 内部逻辑执行函数,不处理锁,可以安全地递归调用 +func getHunyuanStreamModerationInternal(options ...string) bool { + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.HunyuanStreamModeration + } + return false + } + + basename := options[0] + valueInterface, err := prompt.GetSettingFromFilename(basename, "HunyuanStreamModeration") + if err != nil { + log.Println("Error retrieving HunyuanStreamModeration:", err) + return getHunyuanStreamModerationInternal() + } + + value, ok := valueInterface.(bool) + if !ok || !value { + log.Println("Fetching default HunyuanStreamModeration") + return getHunyuanStreamModerationInternal() + } + + return value +} + +// 获取TopPHunyuan值 +func GetTopPHunyuan(options ...string) float64 { + mu.Lock() + defer mu.Unlock() + return getTopPHunyuanInternal(options...) +} + +// 内部逻辑执行函数,不处理锁,可以安全地递归调用 +func getTopPHunyuanInternal(options ...string) float64 { + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.TopPHunyuan + } + return 0.0 + } + + basename := options[0] + valueInterface, err := prompt.GetSettingFromFilename(basename, "TopPHunyuan") + if err != nil { + log.Println("Error retrieving TopPHunyuan:", err) + return getTopPHunyuanInternal() + } + + value, ok := valueInterface.(float64) + if !ok || value == 0.0 { + log.Println("Fetching default TopPHunyuan") + return getTopPHunyuanInternal() + } + + return value +} + +// 获取TemperatureHunyuan值 +func GetTemperatureHunyuan(options ...string) float64 { + mu.Lock() + defer mu.Unlock() + return getTemperatureHunyuanInternal(options...) +} + +// 内部逻辑执行函数,不处理锁,可以安全地递归调用 +func getTemperatureHunyuanInternal(options ...string) float64 { + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.TemperatureHunyuan + } + return 0.0 + } + + basename := options[0] + valueInterface, err := prompt.GetSettingFromFilename(basename, "TemperatureHunyuan") + if err != nil { + log.Println("Error retrieving TemperatureHunyuan:", err) + return getTemperatureHunyuanInternal() + } + + value, ok := valueInterface.(float64) + if !ok || value == 0.0 { + log.Println("Fetching default TemperatureHunyuan") + return getTemperatureHunyuanInternal() + } + + return value +} diff --git a/hunyuan/client.go b/hunyuan/client.go index 45501b1..e1c9bc0 100644 --- a/hunyuan/client.go +++ b/hunyuan/client.go @@ -45,6 +45,90 @@ func NewClient(credential common.CredentialIface, region string, clientProfile * return } +func NewChatCompletionsRequest() (request *ChatCompletionsRequest) { + request = &ChatCompletionsRequest{ + BaseRequest: &tchttp.BaseRequest{}, + } + + request.Init().WithApiInfo("hunyuan", APIVersion, "ChatCompletions") + + return +} + +func NewChatCompletionsResponse() (response *ChatCompletionsResponse) { + response = &ChatCompletionsResponse{} + return + +} + +// ChatCompletions +// 腾讯混元大模型是由腾讯研发的大语言模型,具备强大的中文创作能力,复杂语境下的逻辑推理能力,以及可靠的任务执行能力。本接口支持流式或非流式调用,当使用流式调用时为 SSE 协议。 +// +// 1. 本接口暂不支持返回图片内容。 +// +// 2. 默认每种模型单账号限制并发数为 5 路,如您有提高并发限制的需求请 [联系我们](https://cloud.tencent.com/act/event/Online_service) 。 +// +// 3. 请使用 SDK 调用本接口,每种开发语言的 SDK Git 仓库 examples/hunyuan/v20230901/ 目录下有提供示例供参考。SDK 链接在文档下方 “**开发者资源 - SDK**” 部分提供。 +// +// 可能返回的错误码: +// +// FAILEDOPERATION_ENGINEREQUESTTIMEOUT = "FailedOperation.EngineRequestTimeout" +// FAILEDOPERATION_ENGINESERVERERROR = "FailedOperation.EngineServerError" +// FAILEDOPERATION_ENGINESERVERLIMITEXCEEDED = "FailedOperation.EngineServerLimitExceeded" +// FAILEDOPERATION_FREERESOURCEPACKEXHAUSTED = "FailedOperation.FreeResourcePackExhausted" +// FAILEDOPERATION_RESOURCEPACKEXHAUSTED = "FailedOperation.ResourcePackExhausted" +// FAILEDOPERATION_SERVICENOTACTIVATED = "FailedOperation.ServiceNotActivated" +// FAILEDOPERATION_SERVICESTOP = "FailedOperation.ServiceStop" +// FAILEDOPERATION_SERVICESTOPARREARS = "FailedOperation.ServiceStopArrears" +// INTERNALERROR = "InternalError" +// INVALIDPARAMETER = "InvalidParameter" +// INVALIDPARAMETERVALUE = "InvalidParameterValue" +// INVALIDPARAMETERVALUE_MODEL = "InvalidParameterValue.Model" +// LIMITEXCEEDED = "LimitExceeded" +func (c *Client) ChatCompletions(request *ChatCompletionsRequest) (response *ChatCompletionsResponse, err error) { + return c.ChatCompletionsWithContext(context.Background(), request) +} + +// ChatCompletions +// 腾讯混元大模型是由腾讯研发的大语言模型,具备强大的中文创作能力,复杂语境下的逻辑推理能力,以及可靠的任务执行能力。本接口支持流式或非流式调用,当使用流式调用时为 SSE 协议。 +// +// 1. 本接口暂不支持返回图片内容。 +// +// 2. 默认每种模型单账号限制并发数为 5 路,如您有提高并发限制的需求请 [联系我们](https://cloud.tencent.com/act/event/Online_service) 。 +// +// 3. 请使用 SDK 调用本接口,每种开发语言的 SDK Git 仓库 examples/hunyuan/v20230901/ 目录下有提供示例供参考。SDK 链接在文档下方 “**开发者资源 - SDK**” 部分提供。 +// +// 可能返回的错误码: +// +// FAILEDOPERATION_ENGINEREQUESTTIMEOUT = "FailedOperation.EngineRequestTimeout" +// FAILEDOPERATION_ENGINESERVERERROR = "FailedOperation.EngineServerError" +// FAILEDOPERATION_ENGINESERVERLIMITEXCEEDED = "FailedOperation.EngineServerLimitExceeded" +// FAILEDOPERATION_FREERESOURCEPACKEXHAUSTED = "FailedOperation.FreeResourcePackExhausted" +// FAILEDOPERATION_RESOURCEPACKEXHAUSTED = "FailedOperation.ResourcePackExhausted" +// FAILEDOPERATION_SERVICENOTACTIVATED = "FailedOperation.ServiceNotActivated" +// FAILEDOPERATION_SERVICESTOP = "FailedOperation.ServiceStop" +// FAILEDOPERATION_SERVICESTOPARREARS = "FailedOperation.ServiceStopArrears" +// INTERNALERROR = "InternalError" +// INVALIDPARAMETER = "InvalidParameter" +// INVALIDPARAMETERVALUE = "InvalidParameterValue" +// INVALIDPARAMETERVALUE_MODEL = "InvalidParameterValue.Model" +// LIMITEXCEEDED = "LimitExceeded" +func (c *Client) ChatCompletionsWithContext(ctx context.Context, request *ChatCompletionsRequest) (response *ChatCompletionsResponse, err error) { + if request == nil { + request = NewChatCompletionsRequest() + } + + if c.GetCredential() == nil { + return nil, errors.New("ChatCompletions require credential") + } + + request.SetContext(ctx) + + response = NewChatCompletionsResponse() + err = c.Send(request, response) + return +} + func NewChatProRequest() (request *ChatProRequest) { request = &ChatProRequest{ BaseRequest: &tchttp.BaseRequest{}, @@ -62,13 +146,15 @@ func NewChatProResponse() (response *ChatProResponse) { } // ChatPro -// 腾讯混元大模型高级版是由腾讯研发的大语言模型,具备强大的中文创作能力,复杂语境下的逻辑推理能力,以及可靠的任务执行能力。本接口为SSE协议。 +// 注意:本接口将于 5 月 15 日下线;下线后将不再提供文档指引,接口本身可继续调用,建议使用 [hunyuan](https://cloud.tencent.com/document/api/1729/105701) 接入。 // -// 1.本接口暂不支持返回图片内容。 +// 腾讯混元大模型(hunyuan-pro)是由腾讯研发的大语言模型,具备强大的中文创作能力,复杂语境下的逻辑推理能力,以及可靠的任务执行能力。本接口支持流式或非流式调用,当使用流式调用时为 SSE 协议。 // -// 2.默认单账号限制并发数为5路,如您有提高并发限制的需求请 [联系我们](https://cloud.tencent.com/act/event/Online_service) 。 +// 1. 本接口暂不支持返回图片内容。 // -// 3.请使用SDK调用本接口 ,SDK GitHub仓库examples/hunyuan/v20230901/目录有提供[参考示例](https://github.com/TencentCloud/tencentcloud-sdk-python/blob/master/examples/hunyuan/v20230901/chat_std.py)。 +// 2. 默认单账号限制并发数为 5 路,如您有提高并发限制的需求请 [联系我们](https://cloud.tencent.com/act/event/Online_service) 。 +// +// 3. 请使用 SDK 调用本接口,每种开发语言的 SDK Git 仓库 examples/hunyuan/v20230901/ 目录下有提供示例供参考。SDK 链接在文档下方 “**开发者资源 - SDK**” 部分提供。 // // 可能返回的错误码: // @@ -83,19 +169,20 @@ func NewChatProResponse() (response *ChatProResponse) { // INTERNALERROR = "InternalError" // INVALIDPARAMETERVALUE = "InvalidParameterValue" // LIMITEXCEEDED = "LimitExceeded" -// UNSUPPORTEDOPERATION_NONWHITELISTACCOUNT = "UnsupportedOperation.NonWhitelistAccount" func (c *Client) ChatPro(request *ChatProRequest) (response *ChatProResponse, err error) { return c.ChatProWithContext(context.Background(), request) } // ChatPro -// 腾讯混元大模型高级版是由腾讯研发的大语言模型,具备强大的中文创作能力,复杂语境下的逻辑推理能力,以及可靠的任务执行能力。本接口为SSE协议。 +// 注意:本接口将于 5 月 15 日下线;下线后将不再提供文档指引,接口本身可继续调用,建议使用 [hunyuan](https://cloud.tencent.com/document/api/1729/105701) 接入。 +// +// 腾讯混元大模型(hunyuan-pro)是由腾讯研发的大语言模型,具备强大的中文创作能力,复杂语境下的逻辑推理能力,以及可靠的任务执行能力。本接口支持流式或非流式调用,当使用流式调用时为 SSE 协议。 // -// 1.本接口暂不支持返回图片内容。 +// 1. 本接口暂不支持返回图片内容。 // -// 2.默认单账号限制并发数为5路,如您有提高并发限制的需求请 [联系我们](https://cloud.tencent.com/act/event/Online_service) 。 +// 2. 默认单账号限制并发数为 5 路,如您有提高并发限制的需求请 [联系我们](https://cloud.tencent.com/act/event/Online_service) 。 // -// 3.请使用SDK调用本接口 ,SDK GitHub仓库examples/hunyuan/v20230901/目录有提供[参考示例](https://github.com/TencentCloud/tencentcloud-sdk-python/blob/master/examples/hunyuan/v20230901/chat_std.py)。 +// 3. 请使用 SDK 调用本接口,每种开发语言的 SDK Git 仓库 examples/hunyuan/v20230901/ 目录下有提供示例供参考。SDK 链接在文档下方 “**开发者资源 - SDK**” 部分提供。 // // 可能返回的错误码: // @@ -110,7 +197,6 @@ func (c *Client) ChatPro(request *ChatProRequest) (response *ChatProResponse, er // INTERNALERROR = "InternalError" // INVALIDPARAMETERVALUE = "InvalidParameterValue" // LIMITEXCEEDED = "LimitExceeded" -// UNSUPPORTEDOPERATION_NONWHITELISTACCOUNT = "UnsupportedOperation.NonWhitelistAccount" func (c *Client) ChatProWithContext(ctx context.Context, request *ChatProRequest) (response *ChatProResponse, err error) { if request == nil { request = NewChatProRequest() @@ -144,13 +230,15 @@ func NewChatStdResponse() (response *ChatStdResponse) { } // ChatStd -// 腾讯混元大模型标准版是由腾讯研发的大语言模型,具备强大的中文创作能力,复杂语境下的逻辑推理能力,以及可靠的任务执行能力。本接口为SSE协议。 +// 注意:本接口将于 5 月 15 日下线;下线后将不再提供文档指引,接口本身可继续调用,建议使用 [hunyuan](https://cloud.tencent.com/document/api/1729/105701) 接入。 // -// 1.本接口暂不支持返回图片内容。 +// 腾讯混元大模型标准版是由腾讯研发的大语言模型,具备强大的中文创作能力,复杂语境下的逻辑推理能力,以及可靠的任务执行能力。本接口支持流式或非流式调用,当使用流式调用时为 SSE 协议。 // -// 2.默认单账号限制并发数为5路,如您有提高并发限制的需求请 [联系我们](https://cloud.tencent.com/act/event/Online_service) 。 +// 1. 本接口暂不支持返回图片内容。 // -// 3.请使用SDK调用本接口 ,SDK GitHub仓库examples/hunyuan/v20230901/目录有提供[参考示例](https://github.com/TencentCloud/tencentcloud-sdk-python/blob/master/examples/hunyuan/v20230901/chat_std.py)。 +// 2. 默认单账号限制并发数为 5 路,如您有提高并发限制的需求请 [联系我们](https://cloud.tencent.com/act/event/Online_service) 。 +// +// 3. 请使用 SDK 调用本接口,每种开发语言的 SDK Git 仓库 examples/hunyuan/v20230901/ 目录下有提供示例供参考。SDK 链接在文档下方 “**开发者资源 - SDK**” 部分提供。 // // 可能返回的错误码: // @@ -166,19 +254,20 @@ func NewChatStdResponse() (response *ChatStdResponse) { // INVALIDPARAMETER = "InvalidParameter" // INVALIDPARAMETERVALUE = "InvalidParameterValue" // LIMITEXCEEDED = "LimitExceeded" -// UNSUPPORTEDOPERATION_NONWHITELISTACCOUNT = "UnsupportedOperation.NonWhitelistAccount" func (c *Client) ChatStd(request *ChatStdRequest) (response *ChatStdResponse, err error) { return c.ChatStdWithContext(context.Background(), request) } // ChatStd -// 腾讯混元大模型标准版是由腾讯研发的大语言模型,具备强大的中文创作能力,复杂语境下的逻辑推理能力,以及可靠的任务执行能力。本接口为SSE协议。 +// 注意:本接口将于 5 月 15 日下线;下线后将不再提供文档指引,接口本身可继续调用,建议使用 [hunyuan](https://cloud.tencent.com/document/api/1729/105701) 接入。 +// +// 腾讯混元大模型标准版是由腾讯研发的大语言模型,具备强大的中文创作能力,复杂语境下的逻辑推理能力,以及可靠的任务执行能力。本接口支持流式或非流式调用,当使用流式调用时为 SSE 协议。 // -// 1.本接口暂不支持返回图片内容。 +// 1. 本接口暂不支持返回图片内容。 // -// 2.默认单账号限制并发数为5路,如您有提高并发限制的需求请 [联系我们](https://cloud.tencent.com/act/event/Online_service) 。 +// 2. 默认单账号限制并发数为 5 路,如您有提高并发限制的需求请 [联系我们](https://cloud.tencent.com/act/event/Online_service) 。 // -// 3.请使用SDK调用本接口 ,SDK GitHub仓库examples/hunyuan/v20230901/目录有提供[参考示例](https://github.com/TencentCloud/tencentcloud-sdk-python/blob/master/examples/hunyuan/v20230901/chat_std.py)。 +// 3. 请使用 SDK 调用本接口,每种开发语言的 SDK Git 仓库 examples/hunyuan/v20230901/ 目录下有提供示例供参考。SDK 链接在文档下方 “**开发者资源 - SDK**” 部分提供。 // // 可能返回的错误码: // @@ -194,7 +283,6 @@ func (c *Client) ChatStd(request *ChatStdRequest) (response *ChatStdResponse, er // INVALIDPARAMETER = "InvalidParameter" // INVALIDPARAMETERVALUE = "InvalidParameterValue" // LIMITEXCEEDED = "LimitExceeded" -// UNSUPPORTEDOPERATION_NONWHITELISTACCOUNT = "UnsupportedOperation.NonWhitelistAccount" func (c *Client) ChatStdWithContext(ctx context.Context, request *ChatStdRequest) (response *ChatStdResponse, err error) { if request == nil { request = NewChatStdRequest() @@ -230,7 +318,7 @@ func NewGetEmbeddingResponse() (response *GetEmbeddingResponse) { } // GetEmbedding -// 腾讯混元-Embedding接口,可以将文本转化为高质量的向量数据。 +// 腾讯混元 Embedding 接口,可以将文本转化为高质量的向量数据。 // // 可能返回的错误码: // @@ -243,7 +331,7 @@ func (c *Client) GetEmbedding(request *GetEmbeddingRequest) (response *GetEmbedd } // GetEmbedding -// 腾讯混元-Embedding接口,可以将文本转化为高质量的向量数据。 +// 腾讯混元 Embedding 接口,可以将文本转化为高质量的向量数据。 // // 可能返回的错误码: // @@ -316,3 +404,127 @@ func (c *Client) GetTokenCountWithContext(ctx context.Context, request *GetToken err = c.Send(request, response) return } + +func NewQueryHunyuanImageJobRequest() (request *QueryHunyuanImageJobRequest) { + request = &QueryHunyuanImageJobRequest{ + BaseRequest: &tchttp.BaseRequest{}, + } + + request.Init().WithApiInfo("hunyuan", APIVersion, "QueryHunyuanImageJob") + + return +} + +func NewQueryHunyuanImageJobResponse() (response *QueryHunyuanImageJobResponse) { + response = &QueryHunyuanImageJobResponse{ + BaseResponse: &tchttp.BaseResponse{}, + } + return + +} + +// QueryHunyuanImageJob +// 混元生图接口基于混元大模型,将根据输入的文本描述,智能生成与之相关的结果图。分为提交任务和查询任务2个接口。 +// +// 提交任务:输入文本等,提交一个混元生图异步任务,获得任务 ID。 +// +// 查询任务:根据任务 ID 查询任务的处理状态、处理结果,任务处理完成后可获得生成图像结果。 +// +// 并发任务数(并发)说明:并发任务数指能同时处理的任务数量。混元生图默认提供1个并发任务数,代表最多能同时处理1个已提交的任务,上一个任务处理完毕后才能开始处理下一个任务。 +// +// 可能返回的错误码: +// +// FAILEDOPERATION = "FailedOperation" +func (c *Client) QueryHunyuanImageJob(request *QueryHunyuanImageJobRequest) (response *QueryHunyuanImageJobResponse, err error) { + return c.QueryHunyuanImageJobWithContext(context.Background(), request) +} + +// QueryHunyuanImageJob +// 混元生图接口基于混元大模型,将根据输入的文本描述,智能生成与之相关的结果图。分为提交任务和查询任务2个接口。 +// +// 提交任务:输入文本等,提交一个混元生图异步任务,获得任务 ID。 +// +// 查询任务:根据任务 ID 查询任务的处理状态、处理结果,任务处理完成后可获得生成图像结果。 +// +// 并发任务数(并发)说明:并发任务数指能同时处理的任务数量。混元生图默认提供1个并发任务数,代表最多能同时处理1个已提交的任务,上一个任务处理完毕后才能开始处理下一个任务。 +// +// 可能返回的错误码: +// +// FAILEDOPERATION = "FailedOperation" +func (c *Client) QueryHunyuanImageJobWithContext(ctx context.Context, request *QueryHunyuanImageJobRequest) (response *QueryHunyuanImageJobResponse, err error) { + if request == nil { + request = NewQueryHunyuanImageJobRequest() + } + + if c.GetCredential() == nil { + return nil, errors.New("QueryHunyuanImageJob require credential") + } + + request.SetContext(ctx) + + response = NewQueryHunyuanImageJobResponse() + err = c.Send(request, response) + return +} + +func NewSubmitHunyuanImageJobRequest() (request *SubmitHunyuanImageJobRequest) { + request = &SubmitHunyuanImageJobRequest{ + BaseRequest: &tchttp.BaseRequest{}, + } + + request.Init().WithApiInfo("hunyuan", APIVersion, "SubmitHunyuanImageJob") + + return +} + +func NewSubmitHunyuanImageJobResponse() (response *SubmitHunyuanImageJobResponse) { + response = &SubmitHunyuanImageJobResponse{ + BaseResponse: &tchttp.BaseResponse{}, + } + return + +} + +// SubmitHunyuanImageJob +// 混元生图接口基于混元大模型,将根据输入的文本描述,智能生成与之相关的结果图。分为提交任务和查询任务2个接口。 +// +// 提交任务:输入文本等,提交一个混元生图异步任务,获得任务 ID。 +// +// 查询任务:根据任务 ID 查询任务的处理状态、处理结果,任务处理完成后可获得生成图像结果。 +// +// 并发任务数(并发)说明:并发任务数指能同时处理的任务数量。混元生图默认提供1个并发任务数,代表最多能同时处理1个已提交的任务,上一个任务处理完毕后才能开始处理下一个任务。 +// +// 可能返回的错误码: +// +// FAILEDOPERATION = "FailedOperation" +func (c *Client) SubmitHunyuanImageJob(request *SubmitHunyuanImageJobRequest) (response *SubmitHunyuanImageJobResponse, err error) { + return c.SubmitHunyuanImageJobWithContext(context.Background(), request) +} + +// SubmitHunyuanImageJob +// 混元生图接口基于混元大模型,将根据输入的文本描述,智能生成与之相关的结果图。分为提交任务和查询任务2个接口。 +// +// 提交任务:输入文本等,提交一个混元生图异步任务,获得任务 ID。 +// +// 查询任务:根据任务 ID 查询任务的处理状态、处理结果,任务处理完成后可获得生成图像结果。 +// +// 并发任务数(并发)说明:并发任务数指能同时处理的任务数量。混元生图默认提供1个并发任务数,代表最多能同时处理1个已提交的任务,上一个任务处理完毕后才能开始处理下一个任务。 +// +// 可能返回的错误码: +// +// FAILEDOPERATION = "FailedOperation" +func (c *Client) SubmitHunyuanImageJobWithContext(ctx context.Context, request *SubmitHunyuanImageJobRequest) (response *SubmitHunyuanImageJobResponse, err error) { + if request == nil { + request = NewSubmitHunyuanImageJobRequest() + } + + if c.GetCredential() == nil { + return nil, errors.New("SubmitHunyuanImageJob require credential") + } + + request.SetContext(ctx) + + response = NewSubmitHunyuanImageJobResponse() + err = c.Send(request, response) + return +} diff --git a/hunyuan/errors.go b/hunyuan/errors.go index 6bd7ab4..d284c66 100644 --- a/hunyuan/errors.go +++ b/hunyuan/errors.go @@ -53,9 +53,9 @@ const ( // 参数取值错误。 INVALIDPARAMETERVALUE = "InvalidParameterValue" + // 模型不存在。 + INVALIDPARAMETERVALUE_MODEL = "InvalidParameterValue.Model" + // 超过配额限制。 LIMITEXCEEDED = "LimitExceeded" - - // 非白名单帐号,请前往控制台申请试用。 - UNSUPPORTEDOPERATION_NONWHITELISTACCOUNT = "UnsupportedOperation.NonWhitelistAccount" ) diff --git a/hunyuan/models.go b/hunyuan/models.go index 82e3e97..0f9a653 100644 --- a/hunyuan/models.go +++ b/hunyuan/models.go @@ -20,26 +20,225 @@ import ( "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/json" ) +// Predefined struct for user +type ChatCompletionsRequestParams struct { + // 模型名称,可选值包括 hunyuan-lite、hunyuan-standard、hunyuan-standard-256K、hunyuan-pro。 + // 各模型介绍请阅读 [产品概述](https://cloud.tencent.com/document/product/1729/104753) 中的说明。 + // + // 注意: + // 不同的模型计费不同,请根据 [购买指南](https://cloud.tencent.com/document/product/1729/97731) 按需调用。 + Model *string `json:"Model,omitnil,omitempty" name:"Model"` + + // 聊天上下文信息。 + // 说明: + // 1. 长度最多为 40,按对话时间从旧到新在数组中排列。 + // 2. Message.Role 可选值:system、user、assistant。 + // 其中,system 角色可选,如存在则必须位于列表的最开始。user 和 assistant 需交替出现(一问一答),以 user 提问开始和结束,且 Content 不能为空。Role 的顺序示例:[system(可选) user assistant user assistant user ...]。 + // 3. Messages 中 Content 总长度不能超过模型输入长度上限(可参考 [产品概述](https://cloud.tencent.com/document/product/1729/104753) 文档),超过则会截断最前面的内容,只保留尾部内容。 + Messages []*Message `json:"Messages,omitnil,omitempty" name:"Messages"` + + // 流式调用开关。 + // 说明: + // 1. 未传值时默认为非流式调用(false)。 + // 2. 流式调用时以 SSE 协议增量返回结果(返回值取 Choices[n].Delta 中的值,需要拼接增量数据才能获得完整结果)。 + // 3. 非流式调用时: + // 调用方式与普通 HTTP 请求无异。 + // 接口响应耗时较长,**如需更低时延建议设置为 true**。 + // 只返回一次最终结果(返回值取 Choices[n].Message 中的值)。 + // + // 注意: + // 通过 SDK 调用时,流式和非流式调用需用**不同的方式**获取返回值,具体参考 SDK 中的注释或示例(在各语言 SDK 代码仓库的 examples/hunyuan/v20230901/ 目录中)。 + Stream *bool `json:"Stream,omitnil,omitempty" name:"Stream"` + + // 流式输出审核开关。 + // 说明: + // 1. 当使用流式输出(Stream 字段值为 true)时,该字段生效。 + // 2. 输出审核有流式和同步两种模式,**流式模式首包响应更快**。未传值时默认为流式模式(true)。 + // 3. 如果值为 true,将对输出内容进行分段审核,审核通过的内容流式输出返回。如果出现审核不过,响应中的 FinishReason 值为 sensitive。 + // 4. 如果值为 false,则不使用流式输出审核,需要审核完所有输出内容后再返回结果。 + // + // 注意: + // 当选择流式输出审核时,可能会出现部分内容已输出,但中间某一段响应中的 FinishReason 值为 sensitive,此时说明安全审核未通过。如果业务场景有实时文字上屏的需求,需要自行撤回已上屏的内容,并建议自定义替换为一条提示语,如 “这个问题我不方便回答,不如我们换个话题试试”,以保障终端体验。 + StreamModeration *bool `json:"StreamModeration,omitnil,omitempty" name:"StreamModeration"` + + // 说明: + // 1. 影响输出文本的多样性,取值越大,生成文本的多样性越强。 + // 2. 默认 1.0,取值区间为 [0.0, 1.0]。 + // 3. 非必要不建议使用,不合理的取值会影响效果。 + TopP *float64 `json:"TopP,omitnil,omitempty" name:"TopP"` + + // 说明: + // 1. 较高的数值会使输出更加随机,而较低的数值会使其更加集中和确定。 + // 2. 默认 1.0,取值区间为 [0.0, 2.0]。 + // 3. 非必要不建议使用,不合理的取值会影响效果。 + Temperature *float64 `json:"Temperature,omitnil,omitempty" name:"Temperature"` +} + +type ChatCompletionsRequest struct { + *tchttp.BaseRequest + + // 模型名称,可选值包括 hunyuan-lite、hunyuan-standard、hunyuan-standard-256K、hunyuan-pro。 + // 各模型介绍请阅读 [产品概述](https://cloud.tencent.com/document/product/1729/104753) 中的说明。 + // + // 注意: + // 不同的模型计费不同,请根据 [购买指南](https://cloud.tencent.com/document/product/1729/97731) 按需调用。 + Model *string `json:"Model,omitnil,omitempty" name:"Model"` + + // 聊天上下文信息。 + // 说明: + // 1. 长度最多为 40,按对话时间从旧到新在数组中排列。 + // 2. Message.Role 可选值:system、user、assistant。 + // 其中,system 角色可选,如存在则必须位于列表的最开始。user 和 assistant 需交替出现(一问一答),以 user 提问开始和结束,且 Content 不能为空。Role 的顺序示例:[system(可选) user assistant user assistant user ...]。 + // 3. Messages 中 Content 总长度不能超过模型输入长度上限(可参考 [产品概述](https://cloud.tencent.com/document/product/1729/104753) 文档),超过则会截断最前面的内容,只保留尾部内容。 + Messages []*Message `json:"Messages,omitnil,omitempty" name:"Messages"` + + // 流式调用开关。 + // 说明: + // 1. 未传值时默认为非流式调用(false)。 + // 2. 流式调用时以 SSE 协议增量返回结果(返回值取 Choices[n].Delta 中的值,需要拼接增量数据才能获得完整结果)。 + // 3. 非流式调用时: + // 调用方式与普通 HTTP 请求无异。 + // 接口响应耗时较长,**如需更低时延建议设置为 true**。 + // 只返回一次最终结果(返回值取 Choices[n].Message 中的值)。 + // + // 注意: + // 通过 SDK 调用时,流式和非流式调用需用**不同的方式**获取返回值,具体参考 SDK 中的注释或示例(在各语言 SDK 代码仓库的 examples/hunyuan/v20230901/ 目录中)。 + Stream *bool `json:"Stream,omitnil,omitempty" name:"Stream"` + + // 流式输出审核开关。 + // 说明: + // 1. 当使用流式输出(Stream 字段值为 true)时,该字段生效。 + // 2. 输出审核有流式和同步两种模式,**流式模式首包响应更快**。未传值时默认为流式模式(true)。 + // 3. 如果值为 true,将对输出内容进行分段审核,审核通过的内容流式输出返回。如果出现审核不过,响应中的 FinishReason 值为 sensitive。 + // 4. 如果值为 false,则不使用流式输出审核,需要审核完所有输出内容后再返回结果。 + // + // 注意: + // 当选择流式输出审核时,可能会出现部分内容已输出,但中间某一段响应中的 FinishReason 值为 sensitive,此时说明安全审核未通过。如果业务场景有实时文字上屏的需求,需要自行撤回已上屏的内容,并建议自定义替换为一条提示语,如 “这个问题我不方便回答,不如我们换个话题试试”,以保障终端体验。 + StreamModeration *bool `json:"StreamModeration,omitnil,omitempty" name:"StreamModeration"` + + // 说明: + // 1. 影响输出文本的多样性,取值越大,生成文本的多样性越强。 + // 2. 默认 1.0,取值区间为 [0.0, 1.0]。 + // 3. 非必要不建议使用,不合理的取值会影响效果。 + TopP *float64 `json:"TopP,omitnil,omitempty" name:"TopP"` + + // 说明: + // 1. 较高的数值会使输出更加随机,而较低的数值会使其更加集中和确定。 + // 2. 默认 1.0,取值区间为 [0.0, 2.0]。 + // 3. 非必要不建议使用,不合理的取值会影响效果。 + Temperature *float64 `json:"Temperature,omitnil,omitempty" name:"Temperature"` +} + +func (r *ChatCompletionsRequest) ToJsonString() string { + b, _ := json.Marshal(r) + return string(b) +} + +// FromJsonString It is highly **NOT** recommended to use this function +// because it has no param check, nor strict type check +func (r *ChatCompletionsRequest) FromJsonString(s string) error { + f := make(map[string]interface{}) + if err := json.Unmarshal([]byte(s), &f); err != nil { + return err + } + delete(f, "Model") + delete(f, "Messages") + delete(f, "Stream") + delete(f, "StreamModeration") + delete(f, "TopP") + delete(f, "Temperature") + if len(f) > 0 { + return tcerr.NewTencentCloudSDKError("ClientError.BuildRequestError", "ChatCompletionsRequest has unknown keys!", "") + } + return json.Unmarshal([]byte(s), &r) +} + +// Predefined struct for user +type ChatCompletionsResponseParams struct { + // Unix 时间戳,单位为秒。 + Created *int64 `json:"Created,omitnil,omitempty" name:"Created"` + + // Token 统计信息。 + // 按照总 Token 数量计费。 + Usage *Usage `json:"Usage,omitnil,omitempty" name:"Usage"` + + // 免责声明。 + Note *string `json:"Note,omitnil,omitempty" name:"Note"` + + // 本轮对话的 ID。 + Id *string `json:"Id,omitnil,omitempty" name:"Id"` + + // 回复内容。 + Choices []*Choice `json:"Choices,omitnil,omitempty" name:"Choices"` + + // 错误信息。 + // 如果流式返回中服务处理异常,返回该错误信息。 + // 注意:此字段可能返回 null,表示取不到有效值。 + ErrorMsg *ErrorMsg `json:"ErrorMsg,omitnil,omitempty" name:"ErrorMsg"` + + // 唯一请求 ID,由服务端生成,每次请求都会返回(若请求因其他原因未能抵达服务端,则该次请求不会获得 RequestId)。定位问题时需要提供该次请求的 RequestId。本接口为流式响应接口,当请求成功时,RequestId 会被放在 HTTP 响应的 Header "X-TC-RequestId" 中。 + RequestId *string `json:"RequestId,omitnil,omitempty" name:"RequestId"` +} + +type ChatCompletionsResponse struct { + tchttp.BaseSSEResponse `json:"-"` + Response *ChatCompletionsResponseParams `json:"Response"` +} + +func (r *ChatCompletionsResponse) ToJsonString() string { + b, _ := json.Marshal(r) + return string(b) +} + +// FromJsonString It is highly **NOT** recommended to use this function +// because it has no param check, nor strict type check +func (r *ChatCompletionsResponse) FromJsonString(s string) error { + return json.Unmarshal([]byte(s), &r) +} + // Predefined struct for user type ChatProRequestParams struct { // 聊天上下文信息。 // 说明: - // 1.长度最多为40, 按对话时间从旧到新在数组中排列。 - // 2.Message的Role当前可选值:system、user、assistant,其中,system角色是可选的,如果存在,必须位于列表的最开始。user和assistant需要交替出现(一问一答),最后一个为user提问, 且Content不能为空。 - // 3.Messages中Content总长度不超过16000 token,超过则会截断最前面的内容,只保留尾部内容。建议不超过4000 token。 - Messages []*Message `json:"Messages,omitnil" name:"Messages"` + // 1. 长度最多为 40,按对话时间从旧到新在数组中排列。 + // 2. Message.Role 可选值:system、user、assistant。 + // 其中,system 角色可选,如存在则必须位于列表的最开始。user 和 assistant 需交替出现(一问一答),以 user 提问开始和结束,且 Content 不能为空。Role 的顺序示例:[system(可选) user assistant user assistant user ...]。 + // 3. Messages 中 Content 总长度不能超过 hunyuan-pro 模型输入长度上限(可参考 [产品概述](https://cloud.tencent.com/document/product/1729/104753) 文档),超过则会截断最前面的内容,只保留尾部内容。 + Messages []*Message `json:"Messages,omitnil,omitempty" name:"Messages"` + + // 说明: + // 1. 影响输出文本的多样性,取值越大,生成文本的多样性越强。 + // 2. 默认 1.0,取值区间为 [0.0, 1.0]。 + // 3. 非必要不建议使用,不合理的取值会影响效果。 + TopP *float64 `json:"TopP,omitnil,omitempty" name:"TopP"` // 说明: - // 1.影响输出文本的多样性,取值越大,生成文本的多样性越强。 - // 2.默认1.0,取值区间为[0.0, 1.0]。 - // 3.非必要不建议使用, 不合理的取值会影响效果。 - TopP *float64 `json:"TopP,omitnil" name:"TopP"` + // 1. 较高的数值会使输出更加随机,而较低的数值会使其更加集中和确定。 + // 2. 默认 1.0,取值区间为 [0.0, 2.0]。 + // 3. 非必要不建议使用,不合理的取值会影响效果。 + Temperature *float64 `json:"Temperature,omitnil,omitempty" name:"Temperature"` + // 流式调用开关。 // 说明: - // 1.较高的数值会使输出更加随机,而较低的数值会使其更加集中和确定。 - // 2.默认1.0,取值区间为[0.0,2.0]。 - // 3.非必要不建议使用,不合理的取值会影响效果。 - Temperature *float64 `json:"Temperature,omitnil" name:"Temperature"` + // 1. 未传值时默认为流式调用。 + // 2. 流式调用时以 SSE 协议增量返回结果。 + // 3. 非流式调用时接口响应耗时较长,非必要不建议使用。 + // 4. 非流式调用时只返回一次最终结果,调用方式与普通 HTTP 请求无异。 + // + // 注意: + // 通过 SDK 调用时,流式和非流式调用需要用**不同的方式**获取返回值,具体参考 SDK 中的注释或示例(在各语言 SDK 代码仓库的 examples/hunyuan/v20230901/ 目录中)。 + Stream *bool `json:"Stream,omitnil,omitempty" name:"Stream"` + + // 流式输出审核开关。 + // 说明: + // 1. 输出审核有流式和同步两种模式,**流式模式首包响应更快**。 + // 2. 当使用流式输出(Stream 字段值为 true)时,该字段生效。 + // 3. 如果值为 true,将对输出内容进行分段审核,审核通过的内容流式输出返回。如果出现审核不过,响应中的 FinishReason 值为 sensitive。 + // 4. 如果未传值或值为 false,则不使用流式输出审核,需要审核完所有输出内容后再返回结果。 + // + // 注意: + // 当选择流式输出审核时,可能会出现部分内容已输出,但中间某一段响应中的 FinishReason 值为 sensitive,此时说明安全审核未通过。如果业务场景有实时文字上屏的需求,需要自行撤回已上屏的内容,并建议自定义替换为一条提示语,如 “这个问题我不方便回答,不如我们换个话题试试”,以保障终端体验。 + StreamModeration *bool `json:"StreamModeration,omitnil,omitempty" name:"StreamModeration"` } type ChatProRequest struct { @@ -47,22 +246,45 @@ type ChatProRequest struct { // 聊天上下文信息。 // 说明: - // 1.长度最多为40, 按对话时间从旧到新在数组中排列。 - // 2.Message的Role当前可选值:system、user、assistant,其中,system角色是可选的,如果存在,必须位于列表的最开始。user和assistant需要交替出现(一问一答),最后一个为user提问, 且Content不能为空。 - // 3.Messages中Content总长度不超过16000 token,超过则会截断最前面的内容,只保留尾部内容。建议不超过4000 token。 - Messages []*Message `json:"Messages,omitnil" name:"Messages"` + // 1. 长度最多为 40,按对话时间从旧到新在数组中排列。 + // 2. Message.Role 可选值:system、user、assistant。 + // 其中,system 角色可选,如存在则必须位于列表的最开始。user 和 assistant 需交替出现(一问一答),以 user 提问开始和结束,且 Content 不能为空。Role 的顺序示例:[system(可选) user assistant user assistant user ...]。 + // 3. Messages 中 Content 总长度不能超过 hunyuan-pro 模型输入长度上限(可参考 [产品概述](https://cloud.tencent.com/document/product/1729/104753) 文档),超过则会截断最前面的内容,只保留尾部内容。 + Messages []*Message `json:"Messages,omitnil,omitempty" name:"Messages"` + + // 说明: + // 1. 影响输出文本的多样性,取值越大,生成文本的多样性越强。 + // 2. 默认 1.0,取值区间为 [0.0, 1.0]。 + // 3. 非必要不建议使用,不合理的取值会影响效果。 + TopP *float64 `json:"TopP,omitnil,omitempty" name:"TopP"` // 说明: - // 1.影响输出文本的多样性,取值越大,生成文本的多样性越强。 - // 2.默认1.0,取值区间为[0.0, 1.0]。 - // 3.非必要不建议使用, 不合理的取值会影响效果。 - TopP *float64 `json:"TopP,omitnil" name:"TopP"` + // 1. 较高的数值会使输出更加随机,而较低的数值会使其更加集中和确定。 + // 2. 默认 1.0,取值区间为 [0.0, 2.0]。 + // 3. 非必要不建议使用,不合理的取值会影响效果。 + Temperature *float64 `json:"Temperature,omitnil,omitempty" name:"Temperature"` + // 流式调用开关。 // 说明: - // 1.较高的数值会使输出更加随机,而较低的数值会使其更加集中和确定。 - // 2.默认1.0,取值区间为[0.0,2.0]。 - // 3.非必要不建议使用,不合理的取值会影响效果。 - Temperature *float64 `json:"Temperature,omitnil" name:"Temperature"` + // 1. 未传值时默认为流式调用。 + // 2. 流式调用时以 SSE 协议增量返回结果。 + // 3. 非流式调用时接口响应耗时较长,非必要不建议使用。 + // 4. 非流式调用时只返回一次最终结果,调用方式与普通 HTTP 请求无异。 + // + // 注意: + // 通过 SDK 调用时,流式和非流式调用需要用**不同的方式**获取返回值,具体参考 SDK 中的注释或示例(在各语言 SDK 代码仓库的 examples/hunyuan/v20230901/ 目录中)。 + Stream *bool `json:"Stream,omitnil,omitempty" name:"Stream"` + + // 流式输出审核开关。 + // 说明: + // 1. 输出审核有流式和同步两种模式,**流式模式首包响应更快**。 + // 2. 当使用流式输出(Stream 字段值为 true)时,该字段生效。 + // 3. 如果值为 true,将对输出内容进行分段审核,审核通过的内容流式输出返回。如果出现审核不过,响应中的 FinishReason 值为 sensitive。 + // 4. 如果未传值或值为 false,则不使用流式输出审核,需要审核完所有输出内容后再返回结果。 + // + // 注意: + // 当选择流式输出审核时,可能会出现部分内容已输出,但中间某一段响应中的 FinishReason 值为 sensitive,此时说明安全审核未通过。如果业务场景有实时文字上屏的需求,需要自行撤回已上屏的内容,并建议自定义替换为一条提示语,如 “这个问题我不方便回答,不如我们换个话题试试”,以保障终端体验。 + StreamModeration *bool `json:"StreamModeration,omitnil,omitempty" name:"StreamModeration"` } func (r *ChatProRequest) ToJsonString() string { @@ -80,36 +302,100 @@ func (r *ChatProRequest) FromJsonString(s string) error { delete(f, "Messages") delete(f, "TopP") delete(f, "Temperature") + delete(f, "Stream") + delete(f, "StreamModeration") if len(f) > 0 { return tcerr.NewTencentCloudSDKError("ClientError.BuildRequestError", "ChatProRequest has unknown keys!", "") } return json.Unmarshal([]byte(s), &r) } +// Predefined struct for user +type ChatProResponseParams struct { + // Unix 时间戳,单位为秒。 + Created *int64 `json:"Created,omitnil,omitempty" name:"Created"` + + // Token 统计信息。 + // 按照总 Token 数量计费。 + Usage *Usage `json:"Usage,omitnil,omitempty" name:"Usage"` + + // 免责声明。 + Note *string `json:"Note,omitnil,omitempty" name:"Note"` + + // 本轮对话的 ID。 + Id *string `json:"Id,omitnil,omitempty" name:"Id"` + + // 回复内容。 + Choices []*Choice `json:"Choices,omitnil,omitempty" name:"Choices"` + + // 错误信息。 + // 如果流式返回中服务处理异常,返回该错误信息。 + // 注意:此字段可能返回 null,表示取不到有效值。 + ErrorMsg *ErrorMsg `json:"ErrorMsg,omitnil,omitempty" name:"ErrorMsg"` + + // 唯一请求 ID,由服务端生成,每次请求都会返回(若请求因其他原因未能抵达服务端,则该次请求不会获得 RequestId)。定位问题时需要提供该次请求的 RequestId。本接口为流式响应接口,当请求成功时,RequestId 会被放在 HTTP 响应的 Header "X-TC-RequestId" 中。 + RequestId *string `json:"RequestId,omitnil,omitempty" name:"RequestId"` +} + type ChatProResponse struct { - tchttp.BaseSSEResponse + tchttp.BaseSSEResponse `json:"-"` + Response *ChatProResponseParams `json:"Response"` +} + +func (r *ChatProResponse) ToJsonString() string { + b, _ := json.Marshal(r) + return string(b) +} + +// FromJsonString It is highly **NOT** recommended to use this function +// because it has no param check, nor strict type check +func (r *ChatProResponse) FromJsonString(s string) error { + return json.Unmarshal([]byte(s), &r) } // Predefined struct for user type ChatStdRequestParams struct { // 聊天上下文信息。 // 说明: - // 1.长度最多为40, 按对话时间从旧到新在数组中排列。 - // 2.Message的Role当前可选值:user、assistant,其中,user和assistant需要交替出现(一问一答),最后一个为user提问, 且Content不能为空。 - // 3.Messages中Content总长度不超过16000 token,超过则会截断最前面的内容,只保留尾部内容。建议不超过4000 token。 - Messages []*Message `json:"Messages,omitnil" name:"Messages"` + // 1. 长度最多为 40,按对话时间从旧到新在数组中排列。 + // 2. Message 的 Role 当前可选值:user、assistant。 + // 其中,user 和 assistant 需交替出现(一问一答),以 user 提问开始和结束,且 Content 不能为空。Role 的顺序示例:[user assistant user assistant user ...]。 + // 3. Messages 中 Content 总长度不能超过 hunyuan-standard 模型输入长度上限(可参考 [产品概述](https://cloud.tencent.com/document/product/1729/104753) 文档),超过则会截断最前面的内容,只保留尾部内容。 + Messages []*Message `json:"Messages,omitnil,omitempty" name:"Messages"` + + // 说明: + // 1. 影响输出文本的多样性,取值越大,生成文本的多样性越强。 + // 2. 默认 1.0,取值区间为 [0.0, 1.0]。 + // 3. 非必要不建议使用,不合理的取值会影响效果。 + TopP *float64 `json:"TopP,omitnil,omitempty" name:"TopP"` // 说明: - // 1.影响输出文本的多样性,取值越大,生成文本的多样性越强。 - // 2.默认1.0,取值区间为[0.0, 1.0]。 - // 3.非必要不建议使用, 不合理的取值会影响效果。 - TopP *float64 `json:"TopP,omitnil" name:"TopP"` + // 1. 较高的数值会使输出更加随机,而较低的数值会使其更加集中和确定。 + // 2. 默认 1.0,取值区间为 [0.0, 2.0]。 + // 3. 非必要不建议使用,不合理的取值会影响效果。 + Temperature *float64 `json:"Temperature,omitnil,omitempty" name:"Temperature"` + // 流式调用开关。 // 说明: - // 1.较高的数值会使输出更加随机,而较低的数值会使其更加集中和确定。 - // 2.默认1.0,取值区间为[0.0,2.0]。 - // 3.非必要不建议使用,不合理的取值会影响效果。 - Temperature *float64 `json:"Temperature,omitnil" name:"Temperature"` + // 1. 未传值时默认为流式调用。 + // 2. 流式调用时以 SSE 协议增量返回结果。 + // 3. 非流式调用时接口响应耗时较长,非必要不建议使用。 + // 4. 非流式调用时只返回一次最终结果,调用方式与普通 HTTP 请求无异。 + // + // 注意: + // 通过 SDK 调用时,流式和非流式调用需要用**不同的方式**获取返回值,具体参考 SDK 中的注释或示例(在各语言 SDK 代码仓库的 examples/hunyuan/v20230901/ 目录中)。 + Stream *bool `json:"Stream,omitnil,omitempty" name:"Stream"` + + // 流式输出审核开关。 + // 说明: + // 1. 输出审核有流式和同步两种模式,**流式模式首包响应更快**。 + // 2. 当使用流式输出(Stream 字段值为 true)时,该字段生效。 + // 3. 如果值为 true,将对输出内容进行分段审核,审核通过的内容流式输出返回。如果出现审核不过,响应中的 FinishReason 值为 sensitive。 + // 4. 如果未传值或值为 false,则不使用流式输出审核,需要审核完所有输出内容后再返回结果。 + // + // 注意: + // 当选择流式输出审核时,可能会出现部分内容已输出,但中间某一段响应中的 FinishReason 值为 sensitive,此时说明安全审核未通过。如果业务场景有实时文字上屏的需求,需要自行撤回已上屏的内容,并建议自定义替换为一条提示语,如 “这个问题我不方便回答,不如我们换个话题试试”,以保障终端体验。 + StreamModeration *bool `json:"StreamModeration,omitnil,omitempty" name:"StreamModeration"` } type ChatStdRequest struct { @@ -117,22 +403,45 @@ type ChatStdRequest struct { // 聊天上下文信息。 // 说明: - // 1.长度最多为40, 按对话时间从旧到新在数组中排列。 - // 2.Message的Role当前可选值:user、assistant,其中,user和assistant需要交替出现(一问一答),最后一个为user提问, 且Content不能为空。 - // 3.Messages中Content总长度不超过16000 token,超过则会截断最前面的内容,只保留尾部内容。建议不超过4000 token。 - Messages []*Message `json:"Messages,omitnil" name:"Messages"` + // 1. 长度最多为 40,按对话时间从旧到新在数组中排列。 + // 2. Message 的 Role 当前可选值:user、assistant。 + // 其中,user 和 assistant 需交替出现(一问一答),以 user 提问开始和结束,且 Content 不能为空。Role 的顺序示例:[user assistant user assistant user ...]。 + // 3. Messages 中 Content 总长度不能超过 hunyuan-standard 模型输入长度上限(可参考 [产品概述](https://cloud.tencent.com/document/product/1729/104753) 文档),超过则会截断最前面的内容,只保留尾部内容。 + Messages []*Message `json:"Messages,omitnil,omitempty" name:"Messages"` + + // 说明: + // 1. 影响输出文本的多样性,取值越大,生成文本的多样性越强。 + // 2. 默认 1.0,取值区间为 [0.0, 1.0]。 + // 3. 非必要不建议使用,不合理的取值会影响效果。 + TopP *float64 `json:"TopP,omitnil,omitempty" name:"TopP"` // 说明: - // 1.影响输出文本的多样性,取值越大,生成文本的多样性越强。 - // 2.默认1.0,取值区间为[0.0, 1.0]。 - // 3.非必要不建议使用, 不合理的取值会影响效果。 - TopP *float64 `json:"TopP,omitnil" name:"TopP"` + // 1. 较高的数值会使输出更加随机,而较低的数值会使其更加集中和确定。 + // 2. 默认 1.0,取值区间为 [0.0, 2.0]。 + // 3. 非必要不建议使用,不合理的取值会影响效果。 + Temperature *float64 `json:"Temperature,omitnil,omitempty" name:"Temperature"` + // 流式调用开关。 // 说明: - // 1.较高的数值会使输出更加随机,而较低的数值会使其更加集中和确定。 - // 2.默认1.0,取值区间为[0.0,2.0]。 - // 3.非必要不建议使用,不合理的取值会影响效果。 - Temperature *float64 `json:"Temperature,omitnil" name:"Temperature"` + // 1. 未传值时默认为流式调用。 + // 2. 流式调用时以 SSE 协议增量返回结果。 + // 3. 非流式调用时接口响应耗时较长,非必要不建议使用。 + // 4. 非流式调用时只返回一次最终结果,调用方式与普通 HTTP 请求无异。 + // + // 注意: + // 通过 SDK 调用时,流式和非流式调用需要用**不同的方式**获取返回值,具体参考 SDK 中的注释或示例(在各语言 SDK 代码仓库的 examples/hunyuan/v20230901/ 目录中)。 + Stream *bool `json:"Stream,omitnil,omitempty" name:"Stream"` + + // 流式输出审核开关。 + // 说明: + // 1. 输出审核有流式和同步两种模式,**流式模式首包响应更快**。 + // 2. 当使用流式输出(Stream 字段值为 true)时,该字段生效。 + // 3. 如果值为 true,将对输出内容进行分段审核,审核通过的内容流式输出返回。如果出现审核不过,响应中的 FinishReason 值为 sensitive。 + // 4. 如果未传值或值为 false,则不使用流式输出审核,需要审核完所有输出内容后再返回结果。 + // + // 注意: + // 当选择流式输出审核时,可能会出现部分内容已输出,但中间某一段响应中的 FinishReason 值为 sensitive,此时说明安全审核未通过。如果业务场景有实时文字上屏的需求,需要自行撤回已上屏的内容,并建议自定义替换为一条提示语,如 “这个问题我不方便回答,不如我们换个话题试试”,以保障终端体验。 + StreamModeration *bool `json:"StreamModeration,omitnil,omitempty" name:"StreamModeration"` } func (r *ChatStdRequest) ToJsonString() string { @@ -150,75 +459,122 @@ func (r *ChatStdRequest) FromJsonString(s string) error { delete(f, "Messages") delete(f, "TopP") delete(f, "Temperature") + delete(f, "Stream") + delete(f, "StreamModeration") if len(f) > 0 { return tcerr.NewTencentCloudSDKError("ClientError.BuildRequestError", "ChatStdRequest has unknown keys!", "") } return json.Unmarshal([]byte(s), &r) } +// Predefined struct for user +type ChatStdResponseParams struct { + // Unix 时间戳,单位为秒。 + Created *int64 `json:"Created,omitnil,omitempty" name:"Created"` + + // Token 统计信息。 + // 按照总 Token 数量计费。 + Usage *Usage `json:"Usage,omitnil,omitempty" name:"Usage"` + + // 免责声明。 + Note *string `json:"Note,omitnil,omitempty" name:"Note"` + + // 本轮对话的 ID。 + Id *string `json:"Id,omitnil,omitempty" name:"Id"` + + // 回复内容。 + Choices []*Choice `json:"Choices,omitnil,omitempty" name:"Choices"` + + // 错误信息。 + // 如果流式返回中服务处理异常,返回该错误信息。 + // 注意:此字段可能返回 null,表示取不到有效值。 + ErrorMsg *ErrorMsg `json:"ErrorMsg,omitnil,omitempty" name:"ErrorMsg"` + + // 唯一请求 ID,由服务端生成,每次请求都会返回(若请求因其他原因未能抵达服务端,则该次请求不会获得 RequestId)。定位问题时需要提供该次请求的 RequestId。本接口为流式响应接口,当请求成功时,RequestId 会被放在 HTTP 响应的 Header "X-TC-RequestId" 中。 + RequestId *string `json:"RequestId,omitnil,omitempty" name:"RequestId"` +} + type ChatStdResponse struct { - tchttp.BaseSSEResponse + tchttp.BaseSSEResponse `json:"-"` + Response *ChatStdResponseParams `json:"Response"` +} + +func (r *ChatStdResponse) ToJsonString() string { + b, _ := json.Marshal(r) + return string(b) +} + +// FromJsonString It is highly **NOT** recommended to use this function +// because it has no param check, nor strict type check +func (r *ChatStdResponse) FromJsonString(s string) error { + return json.Unmarshal([]byte(s), &r) } type Choice struct { - // 流式结束标志位,为 stop 则表示尾包。 - FinishReason *string `json:"FinishReason,omitnil" name:"FinishReason"` + // 结束标志位,可能为 stop 或 sensitive。 + // stop 表示输出正常结束,sensitive 只在开启流式输出审核时会出现,表示安全审核未通过。 + FinishReason *string `json:"FinishReason,omitnil,omitempty" name:"FinishReason"` + + // 增量返回值,流式调用时使用该字段。 + // 注意:此字段可能返回 null,表示取不到有效值。 + Delta *Delta `json:"Delta,omitnil,omitempty" name:"Delta"` - // 返回值。 - Delta *Delta `json:"Delta,omitnil" name:"Delta"` + // 返回值,非流式调用时使用该字段。 + // 注意:此字段可能返回 null,表示取不到有效值。 + Message *Message `json:"Message,omitnil,omitempty" name:"Message"` } type Delta struct { // 角色名称。 - Role *string `json:"Role,omitnil" name:"Role"` + Role *string `json:"Role,omitnil,omitempty" name:"Role"` // 内容详情。 - Content *string `json:"Content,omitnil" name:"Content"` + Content *string `json:"Content,omitnil,omitempty" name:"Content"` } type EmbeddingData struct { // embedding 信息。 // 注意:此字段可能返回 null,表示取不到有效值。 - Embedding []*float64 `json:"Embedding,omitnil" name:"Embedding"` + Embedding []*float64 `json:"Embedding,omitnil,omitempty" name:"Embedding"` // 下标。 // 注意:此字段可能返回 null,表示取不到有效值。 - Index *int64 `json:"Index,omitnil" name:"Index"` + Index *int64 `json:"Index,omitnil,omitempty" name:"Index"` // embedding // 注意:此字段可能返回 null,表示取不到有效值。 - Object *string `json:"Object,omitnil" name:"Object"` + Object *string `json:"Object,omitnil,omitempty" name:"Object"` } type EmbeddingUsage struct { - // 输入Token数。 - PromptTokens *int64 `json:"PromptTokens,omitnil" name:"PromptTokens"` + // 输入 Token 数。 + PromptTokens *int64 `json:"PromptTokens,omitnil,omitempty" name:"PromptTokens"` - // 总Token数。 - TotalTokens *int64 `json:"TotalTokens,omitnil" name:"TotalTokens"` + // 总 Token 数。 + TotalTokens *int64 `json:"TotalTokens,omitnil,omitempty" name:"TotalTokens"` } type ErrorMsg struct { // 错误提示信息。 - Msg *string `json:"Msg,omitnil" name:"Msg"` + Msg *string `json:"Msg,omitnil,omitempty" name:"Msg"` // 错误码。 // 4000 服务内部异常。 // 4001 请求模型超时。 - Code *int64 `json:"Code,omitnil" name:"Code"` + Code *int64 `json:"Code,omitnil,omitempty" name:"Code"` } // Predefined struct for user type GetEmbeddingRequestParams struct { - // 输入文本。总长度不超过1024 个token, 超过则会截断最后面的内容。 - Input *string `json:"Input,omitnil" name:"Input"` + // 输入文本。总长度不超过 1024 个 Token,超过则会截断最后面的内容。 + Input *string `json:"Input,omitnil,omitempty" name:"Input"` } type GetEmbeddingRequest struct { *tchttp.BaseRequest - // 输入文本。总长度不超过1024 个token, 超过则会截断最后面的内容。 - Input *string `json:"Input,omitnil" name:"Input"` + // 输入文本。总长度不超过 1024 个 Token,超过则会截断最后面的内容。 + Input *string `json:"Input,omitnil,omitempty" name:"Input"` } func (r *GetEmbeddingRequest) ToJsonString() string { @@ -242,14 +598,14 @@ func (r *GetEmbeddingRequest) FromJsonString(s string) error { // Predefined struct for user type GetEmbeddingResponseParams struct { - // 返回的 embedding 信息。 - Data []*EmbeddingData `json:"Data,omitnil" name:"Data"` + // 返回的 Embedding 信息。当前不支持批量,所以数组元素数目为 1。 + Data []*EmbeddingData `json:"Data,omitnil,omitempty" name:"Data"` - // token 使用计数,按照总token数量收费。 - Usage *EmbeddingUsage `json:"Usage,omitnil" name:"Usage"` + // Token 使用计数,按照总 Token 数量收费。 + Usage *EmbeddingUsage `json:"Usage,omitnil,omitempty" name:"Usage"` - // 唯一请求 ID,每次请求都会返回。定位问题时需要提供该次请求的 RequestId。 - RequestId *string `json:"RequestId,omitnil" name:"RequestId"` + // 唯一请求 ID,由服务端生成,每次请求都会返回(若请求因其他原因未能抵达服务端,则该次请求不会获得 RequestId)。定位问题时需要提供该次请求的 RequestId。 + RequestId *string `json:"RequestId,omitnil,omitempty" name:"RequestId"` } type GetEmbeddingResponse struct { @@ -271,14 +627,14 @@ func (r *GetEmbeddingResponse) FromJsonString(s string) error { // Predefined struct for user type GetTokenCountRequestParams struct { // 输入文本 - Prompt *string `json:"Prompt,omitnil" name:"Prompt"` + Prompt *string `json:"Prompt,omitnil,omitempty" name:"Prompt"` } type GetTokenCountRequest struct { *tchttp.BaseRequest // 输入文本 - Prompt *string `json:"Prompt,omitnil" name:"Prompt"` + Prompt *string `json:"Prompt,omitnil,omitempty" name:"Prompt"` } func (r *GetTokenCountRequest) ToJsonString() string { @@ -303,16 +659,16 @@ func (r *GetTokenCountRequest) FromJsonString(s string) error { // Predefined struct for user type GetTokenCountResponseParams struct { // token计数 - TokenCount *int64 `json:"TokenCount,omitnil" name:"TokenCount"` + TokenCount *int64 `json:"TokenCount,omitnil,omitempty" name:"TokenCount"` // 字符计数 - CharacterCount *int64 `json:"CharacterCount,omitnil" name:"CharacterCount"` + CharacterCount *int64 `json:"CharacterCount,omitnil,omitempty" name:"CharacterCount"` // 切分后的列表 - Tokens []*string `json:"Tokens,omitnil" name:"Tokens"` + Tokens []*string `json:"Tokens,omitnil,omitempty" name:"Tokens"` - // 唯一请求 ID,每次请求都会返回。定位问题时需要提供该次请求的 RequestId。 - RequestId *string `json:"RequestId,omitnil" name:"RequestId"` + // 唯一请求 ID,由服务端生成,每次请求都会返回(若请求因其他原因未能抵达服务端,则该次请求不会获得 RequestId)。定位问题时需要提供该次请求的 RequestId。 + RequestId *string `json:"RequestId,omitnil,omitempty" name:"RequestId"` } type GetTokenCountResponse struct { @@ -333,19 +689,200 @@ func (r *GetTokenCountResponse) FromJsonString(s string) error { type Message struct { // 角色 - Role *string `json:"Role,omitnil" name:"Role"` + Role *string `json:"Role,omitnil,omitempty" name:"Role"` + + // 文本内容 + Content *string `json:"Content,omitnil,omitempty" name:"Content"` +} + +// Predefined struct for user +type QueryHunyuanImageJobRequestParams struct { + // 任务 ID。 + JobId *string `json:"JobId,omitnil,omitempty" name:"JobId"` +} + +type QueryHunyuanImageJobRequest struct { + *tchttp.BaseRequest + + // 任务 ID。 + JobId *string `json:"JobId,omitnil,omitempty" name:"JobId"` +} + +func (r *QueryHunyuanImageJobRequest) ToJsonString() string { + b, _ := json.Marshal(r) + return string(b) +} + +// FromJsonString It is highly **NOT** recommended to use this function +// because it has no param check, nor strict type check +func (r *QueryHunyuanImageJobRequest) FromJsonString(s string) error { + f := make(map[string]interface{}) + if err := json.Unmarshal([]byte(s), &f); err != nil { + return err + } + delete(f, "JobId") + if len(f) > 0 { + return tcerr.NewTencentCloudSDKError("ClientError.BuildRequestError", "QueryHunyuanImageJobRequest has unknown keys!", "") + } + return json.Unmarshal([]byte(s), &r) +} + +// Predefined struct for user +type QueryHunyuanImageJobResponseParams struct { + // 当前任务状态码: + // 1:等待中、2:运行中、4:处理失败、5:处理完成。 + JobStatusCode *string `json:"JobStatusCode,omitnil,omitempty" name:"JobStatusCode"` + + // 当前任务状态:排队中、处理中、处理失败或者处理完成。 + JobStatusMsg *string `json:"JobStatusMsg,omitnil,omitempty" name:"JobStatusMsg"` + + // 任务处理失败错误码。 + JobErrorCode *string `json:"JobErrorCode,omitnil,omitempty" name:"JobErrorCode"` + + // 任务处理失败错误信息。 + JobErrorMsg *string `json:"JobErrorMsg,omitnil,omitempty" name:"JobErrorMsg"` + + // 生成图 URL 列表,有效期1小时,请及时保存。 + ResultImage []*string `json:"ResultImage,omitnil,omitempty" name:"ResultImage"` + + // 结果 detail 数组,Success 代表成功。 + ResultDetails []*string `json:"ResultDetails,omitnil,omitempty" name:"ResultDetails"` + + // 对应 SubmitTextToImageProJob 接口中 Revise 参数。开启扩写时,返回扩写后的 prompt 文本。 如果关闭扩写,将直接返回原始输入的 prompt。 + RevisedPrompt []*string `json:"RevisedPrompt,omitnil,omitempty" name:"RevisedPrompt"` + + // 唯一请求 ID,由服务端生成,每次请求都会返回(若请求因其他原因未能抵达服务端,则该次请求不会获得 RequestId)。定位问题时需要提供该次请求的 RequestId。 + RequestId *string `json:"RequestId,omitnil,omitempty" name:"RequestId"` +} - // 消息的内容 - Content *string `json:"Content,omitnil" name:"Content"` +type QueryHunyuanImageJobResponse struct { + *tchttp.BaseResponse + Response *QueryHunyuanImageJobResponseParams `json:"Response"` +} + +func (r *QueryHunyuanImageJobResponse) ToJsonString() string { + b, _ := json.Marshal(r) + return string(b) +} + +// FromJsonString It is highly **NOT** recommended to use this function +// because it has no param check, nor strict type check +func (r *QueryHunyuanImageJobResponse) FromJsonString(s string) error { + return json.Unmarshal([]byte(s), &r) +} + +// Predefined struct for user +type SubmitHunyuanImageJobRequestParams struct { + // 文本描述。 算法将根据输入的文本智能生成与之相关的图像。 不能为空,推荐使用中文。最多可传100个 utf-8 字符。 + Prompt *string `json:"Prompt,omitnil,omitempty" name:"Prompt"` + + // 绘画风格。 + // 请在 [混元生图风格列表](https://cloud.tencent.com/document/product/1729/105846) 中选择期望的风格,传入风格编号。 + // 不传默认不指定风格。 + Style *string `json:"Style,omitnil,omitempty" name:"Style"` + + // 生成图分辨率。 + // 支持生成以下分辨率的图片:768:768(1:1)、768:1024(3:4)、1024:768(4:3)、1024:1024(1:1)、720:1280(9:16)、1280:720(16:9)、768:1280(3:5)、1280:768(5:3),不传默认使用1024:1024。 + Resolution *string `json:"Resolution,omitnil,omitempty" name:"Resolution"` + + // 为生成结果图添加显式水印标识的开关,默认为1。 + // 1:添加。 + // 0:不添加。 + // 其他数值:默认按1处理。 + // 建议您使用显著标识来提示结果图使用了 AI 绘画技术,是 AI 生成的图片。 + LogoAdd *int64 `json:"LogoAdd,omitnil,omitempty" name:"LogoAdd"` + + // prompt 扩写开关。1为开启,0为关闭,不传默认开启。 + // 开启扩写后,将自动扩写原始输入的 prompt 并使用扩写后的 prompt 生成图片,返回生成图片结果时将一并返回扩写后的 prompt 文本。 + // 如果关闭扩写,将直接使用原始输入的 prompt 生成图片。 + // 建议开启,在多数场景下可提升生成图片效果、丰富生成图片细节。 + Revise *int64 `json:"Revise,omitnil,omitempty" name:"Revise"` +} + +type SubmitHunyuanImageJobRequest struct { + *tchttp.BaseRequest + + // 文本描述。 算法将根据输入的文本智能生成与之相关的图像。 不能为空,推荐使用中文。最多可传100个 utf-8 字符。 + Prompt *string `json:"Prompt,omitnil,omitempty" name:"Prompt"` + + // 绘画风格。 + // 请在 [混元生图风格列表](https://cloud.tencent.com/document/product/1729/105846) 中选择期望的风格,传入风格编号。 + // 不传默认不指定风格。 + Style *string `json:"Style,omitnil,omitempty" name:"Style"` + + // 生成图分辨率。 + // 支持生成以下分辨率的图片:768:768(1:1)、768:1024(3:4)、1024:768(4:3)、1024:1024(1:1)、720:1280(9:16)、1280:720(16:9)、768:1280(3:5)、1280:768(5:3),不传默认使用1024:1024。 + Resolution *string `json:"Resolution,omitnil,omitempty" name:"Resolution"` + + // 为生成结果图添加显式水印标识的开关,默认为1。 + // 1:添加。 + // 0:不添加。 + // 其他数值:默认按1处理。 + // 建议您使用显著标识来提示结果图使用了 AI 绘画技术,是 AI 生成的图片。 + LogoAdd *int64 `json:"LogoAdd,omitnil,omitempty" name:"LogoAdd"` + + // prompt 扩写开关。1为开启,0为关闭,不传默认开启。 + // 开启扩写后,将自动扩写原始输入的 prompt 并使用扩写后的 prompt 生成图片,返回生成图片结果时将一并返回扩写后的 prompt 文本。 + // 如果关闭扩写,将直接使用原始输入的 prompt 生成图片。 + // 建议开启,在多数场景下可提升生成图片效果、丰富生成图片细节。 + Revise *int64 `json:"Revise,omitnil,omitempty" name:"Revise"` +} + +func (r *SubmitHunyuanImageJobRequest) ToJsonString() string { + b, _ := json.Marshal(r) + return string(b) +} + +// FromJsonString It is highly **NOT** recommended to use this function +// because it has no param check, nor strict type check +func (r *SubmitHunyuanImageJobRequest) FromJsonString(s string) error { + f := make(map[string]interface{}) + if err := json.Unmarshal([]byte(s), &f); err != nil { + return err + } + delete(f, "Prompt") + delete(f, "Style") + delete(f, "Resolution") + delete(f, "LogoAdd") + delete(f, "Revise") + if len(f) > 0 { + return tcerr.NewTencentCloudSDKError("ClientError.BuildRequestError", "SubmitHunyuanImageJobRequest has unknown keys!", "") + } + return json.Unmarshal([]byte(s), &r) +} + +// Predefined struct for user +type SubmitHunyuanImageJobResponseParams struct { + // 任务 ID。 + JobId *string `json:"JobId,omitnil,omitempty" name:"JobId"` + + // 唯一请求 ID,由服务端生成,每次请求都会返回(若请求因其他原因未能抵达服务端,则该次请求不会获得 RequestId)。定位问题时需要提供该次请求的 RequestId。 + RequestId *string `json:"RequestId,omitnil,omitempty" name:"RequestId"` +} + +type SubmitHunyuanImageJobResponse struct { + *tchttp.BaseResponse + Response *SubmitHunyuanImageJobResponseParams `json:"Response"` +} + +func (r *SubmitHunyuanImageJobResponse) ToJsonString() string { + b, _ := json.Marshal(r) + return string(b) +} + +// FromJsonString It is highly **NOT** recommended to use this function +// because it has no param check, nor strict type check +func (r *SubmitHunyuanImageJobResponse) FromJsonString(s string) error { + return json.Unmarshal([]byte(s), &r) } type Usage struct { - // 输入 token 数量。 - PromptTokens *int64 `json:"PromptTokens,omitnil" name:"PromptTokens"` + // 输入 Token 数量。 + PromptTokens *int64 `json:"PromptTokens,omitnil,omitempty" name:"PromptTokens"` - // 输出 token 数量。 - CompletionTokens *int64 `json:"CompletionTokens,omitnil" name:"CompletionTokens"` + // 输出 Token 数量。 + CompletionTokens *int64 `json:"CompletionTokens,omitnil,omitempty" name:"CompletionTokens"` - // 总 token 数量。 - TotalTokens *int64 `json:"TotalTokens,omitnil" name:"TotalTokens"` + // 总 Token 数量。 + TotalTokens *int64 `json:"TotalTokens,omitnil,omitempty" name:"TotalTokens"` } diff --git a/structs/struct.go b/structs/struct.go index 9e01676..a04e96e 100644 --- a/structs/struct.go +++ b/structs/struct.go @@ -264,8 +264,11 @@ type Settings struct { MdPromptKeyboardAtGroup bool `yaml:"mdPromptKeyboardAtGroup"` // 群内使用md能力模拟PromptKeyboard GroupHintWords []string `yaml:"groupHintWords"` - HunyuanType int `yaml:"hunyuanType"` - MaxTokensHunyuan int `yaml:"maxTokensHunyuan"` + HunyuanType int `yaml:"hunyuanType"` + MaxTokensHunyuan int `yaml:"maxTokensHunyuan"` + HunyuanStreamModeration bool `yaml:"hunyuanStreamModeration"` + TopPHunyuan float64 `yaml:"topPHunyuan"` + TemperatureHunyuan float64 `yaml:"temperatureHunyuan"` WenxinAccessToken string `yaml:"wenxinAccessToken"` WenxinApiPath string `yaml:"wenxinApiPath"` diff --git a/template/config_template.go b/template/config_template.go index 4c559a8..215e99a 100644 --- a/template/config_template.go +++ b/template/config_template.go @@ -99,7 +99,10 @@ settings: secretKey : "" region : "" #留空 maxTokensHunyuan : 4096 #最大上下文 - hunyuanType : 0 #0=高级版 1=标准版 价格差异10倍 + hunyuanType : 0 #0=高级版 1=标准版std 2=hunyuan-lite 3=hunyuan-standard 4=hunyuan-standard-256K 5=hunyuan-pro + hunyuanStreamModeration : false #是否采用流式审核 + topPHunyuan : 1.0 #累积概率最高的令牌进行采样的界限 + temperatureHunyuan : 1.0 #生成的随机性控制 #文心配置项 wenxinAccessToken : "" #请求百度access_token接口获取到的,有效期一个月,需要自己请求获取 diff --git a/utils/utils.go b/utils/utils.go index 37f798e..0c57b76 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -70,6 +70,17 @@ func PrintChatStdRequest(request *hunyuan.ChatStdRequest) { } +func PrintChatCompletionsRequest(request *hunyuan.ChatCompletionsRequest) { + + // 打印Messages + for i, msg := range request.Messages { + fmtf.Printf("Message %d:\n", i) + fmtf.Printf("Content: %s\n", *msg.Content) + fmtf.Printf("Role: %s\n", *msg.Role) + } + +} + // contains 检查一个字符串切片是否包含一个特定的字符串 func Contains(slice []string, item string) bool { for _, a := range slice { From d1053df9e9b60c88f20847e1873af8530ef5105e Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Sun, 19 May 2024 14:43:14 +0800 Subject: [PATCH 06/30] Beta120 (#118) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 * beta117 * beta118 * beta119 * beta120 --- applogic/gensokyo.go | 12 ++---- config/config.go | 10 +++++ main.go | 5 +++ readme.md | 28 +++++++----- server/server.go | 45 +++++++++++++++++-- structs/struct.go | 1 + template/config_template.go | 1 + utils/utils.go | 86 ++++++++++++++++++++++++++++++++----- 8 files changed, 156 insertions(+), 32 deletions(-) diff --git a/applogic/gensokyo.go b/applogic/gensokyo.go index 60d5a94..eb5c435 100644 --- a/applogic/gensokyo.go +++ b/applogic/gensokyo.go @@ -206,18 +206,14 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } } - // 读取URL参数 "selfid" - selfid := r.URL.Query().Get("selfid") - if selfid != "" { - // 使用 prompt 变量进行后续处理 - fmt.Printf("收到selfid参数: %s\n", selfid) - } + // 直接从ob11事件获取selfid + selfid := strconv.FormatInt(message.SelfID, 10) // 读取URL参数 "api" api := r.URL.Query().Get("api") - if selfid != "" { + if api != "" { // 使用 prompt 变量进行后续处理 - fmt.Printf("收到api参数: %s\n", selfid) + fmt.Printf("收到api参数: %s\n", api) } // 打印日志信息,包括prompt参数 diff --git a/config/config.go b/config/config.go index c87ae21..5fda48c 100644 --- a/config/config.go +++ b/config/config.go @@ -231,6 +231,16 @@ func IPWhiteList() []string { return nil } +// 获取HttpPaths +func GetHttpPaths() []string { + mu.Lock() + defer mu.Unlock() + if instance != nil { + return instance.Settings.HttpPaths + } + return nil +} + // 获取最大上下文 func GetMaxTokensHunyuan(options ...string) int { mu.Lock() diff --git a/main.go b/main.go index c950854..7de4084 100644 --- a/main.go +++ b/main.go @@ -195,6 +195,11 @@ func main() { log.Fatalf("Failed to load blacklist: %v", err) } + // 判断是否设置多个http地址,获取对应关系 + if len(config.GetHttpPaths()) > 0 { + utils.FetchAndStoreUserIDs() + } + // 启动黑名单文件变动监听 go utils.WatchBlacklist(blacklistPath) diff --git a/readme.md b/readme.md index 6df07f8..f6750d1 100644 --- a/readme.md +++ b/readme.md @@ -32,16 +32,6 @@ _✨ 适用于Gensokyo以及Onebotv11的大模型一键端 ✨_ 并发环境下的sse内存安全,支持维持多用户同时双向sse传输 ---- - -## 效果 - -![效果图](/pic/3.png) - -![效果图](/pic/4.png) - ---- - ## 支持llm平台 腾讯混元 @@ -117,6 +107,14 @@ AhoCorasick算法实现的超高效文本IN-Out替换规则,可大量替换n 支持中间件开发,在gensokyo框架层到gensokyo-llm的http请求之间,可开发中间件实现向量拓展,数据库拓展,动态修改用户问题. +支持反向ws连接,支持同时连接多个onebotv11的http-api + +--- + +## 使用教程 + +施工中...(部署非常简单,有一定onebotv11机器人部署经验者可参考config.yml注释直接部署出来.) + --- # API接口调用说明 @@ -285,6 +283,16 @@ exitOnQ需要enhancedPromptChoices=true,其实enhancedPromptChoices最好就是t promptMarks和switchOnQ、switchOnA在功能上是相同的,都是根据关键字跳转分支,promptMarks先执行,不分轮次不分QA,switchOnQ和switchOnA更具体,区分Q和A,区分轮次,实现细节跳转。 +--- + +## 故事模式效果(QQ开放平台) + +![效果图](/pic/3.png) + +![效果图](/pic/4.png) + +--- + ## 已知问题 如果有固定且不需要切换的分支,请设置该yml的promptMarksLength为99999 diff --git a/server/server.go b/server/server.go index eaa1706..bfcc210 100644 --- a/server/server.go +++ b/server/server.go @@ -30,12 +30,49 @@ var upgrader = websocket.Upgrader{ }, } +// 全局变量和互斥锁 +var ( + selfIDs []string + selfIDsMu sync.Mutex +) + +// AddSelfID 添加一个 ID 到全局切片中 +func AddSelfID(id string) { + selfIDsMu.Lock() + defer selfIDsMu.Unlock() + selfIDs = append(selfIDs, id) +} + +// GetSelfIDs 返回当前保存的所有 ID +func GetSelfIDs() []string { + selfIDsMu.Lock() + defer selfIDsMu.Unlock() + // 返回切片的副本以防止外部修改 + copiedIDs := make([]string, len(selfIDs)) + copy(copiedIDs, selfIDs) + return copiedIDs +} + +// IsSelfIDExists 检查一个 ID 是否存在于全局切片中 +func IsSelfIDExists(id string) bool { + selfIDsMu.Lock() + defer selfIDsMu.Unlock() + for _, sid := range selfIDs { + if sid == id { + return true + } + } + return false +} + // 用于处理WebSocket连接 func WsHandler(w http.ResponseWriter, r *http.Request, config *config.Config) { // 从请求头或URL查询参数中提取token tokenFromHeader := r.Header.Get("Authorization") selfID := r.Header.Get("X-Self-ID") fmtf.Printf("接入机器人X-Self-ID[%v]", selfID) + // 加入到数组里 + AddSelfID(selfID) var token string if strings.HasPrefix(tokenFromHeader, "Token ") { token = strings.TrimPrefix(tokenFromHeader, "Token ") @@ -86,13 +123,13 @@ func WsHandler(w http.ResponseWriter, r *http.Request, config *config.Config) { } if messageType == websocket.TextMessage { - processWSMessage(p, selfID) + processWSMessage(p) } } } // 处理收到的信息 -func processWSMessage(msg []byte, selfid string) { +func processWSMessage(msg []byte) { var genericMap map[string]interface{} if err := json.Unmarshal(msg, &genericMap); err != nil { log.Printf("Error unmarshalling message to map: %v, Original message: %s\n", err, string(msg)) @@ -131,9 +168,9 @@ func processWSMessage(msg []byte, selfid string) { // 构造请求URL var url string if config.GetLotus() == "" { - url = "http://127.0.0.1:" + fmt.Sprint(port) + "/gensokyo?selfid=" + selfid + url = "http://127.0.0.1:" + fmt.Sprint(port) + "/gensokyo" } else { - url = config.GetLotus() + "/gensokyo?selfid=" + selfid + url = config.GetLotus() + "/gensokyo" } // 创建POST请求 diff --git a/structs/struct.go b/structs/struct.go index a04e96e..3c9cebd 100644 --- a/structs/struct.go +++ b/structs/struct.go @@ -254,6 +254,7 @@ type Settings struct { Port int `yaml:"port"` SelfPath string `yaml:"selfPath"` HttpPath string `yaml:"path"` + HttpPaths []string `yaml:"paths"` Lotus string `yaml:"lotus"` PathToken string `yaml:"pathToken"` SystemPrompt []string `yaml:"systemPrompt"` diff --git a/template/config_template.go b/template/config_template.go index 215e99a..d258476 100644 --- a/template/config_template.go +++ b/template/config_template.go @@ -10,6 +10,7 @@ settings: port : 46233 #本程序监听端口,支持gensokyo http上报, 请在gensokyo的反向http配置加入 post_url: ["http://127.0.0.1:port/gensokyo"] selfPath : "" #本程序监听地址,不包含http头,请放通port到公网,仅发图场景需要填写,可以是域名,暂不支持https. path : "http://123.123.123.123:11111" #调用gensokyo api的地址,填入 gensokyo 的 正向http地址 http_address: "0.0.0.0:46231" 对应填入 "http://127.0.0.1:46231" + paths : [] #当要连接多个onebotv11的http正向地址时,多个地址填入这里. lotus : "" #当填写另一个gensokyo-llm的http地址时,将请求另一个的conversation端点,实现多个llm不需要多次配置,简化配置,单独使用请忽略留空.例:http://192.168.0.1:12345(包含http头和端口) pathToken : "" #gensokyo正向http-api的access_token(是onebotv11标准的) apiType : 0 #0=混元 1=文心(文心平台包含了N种模型...) 2=gpt 3=rwkv 4=通义千问 5=智谱AI diff --git a/utils/utils.go b/utils/utils.go index 0c57b76..f543ef5 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -44,6 +44,57 @@ type MessageIDInfo struct { var UserIDMessageIDs = make(map[int64][]MessageIDInfo) var muUserIDMessageIDs sync.RWMutex // 用于UserIDMessageIDs的读写锁 +var ( + baseURLMap = make(map[string]string) + baseURLMapMu sync.Mutex +) + +// 结构体用于解析 JSON 响应 +type loginInfoResponse struct { + Status string `json:"status"` + Retcode int `json:"retcode"` + Data struct { + UserID int64 `json:"user_id"` + Nickname string `json:"nickname"` + } `json:"data"` +} + +// 构造 URL 并请求数据 +func FetchAndStoreUserIDs() { + httpPaths := config.GetHttpPaths() + for _, baseURL := range httpPaths { + url := baseURL + "/get_login_info" + resp, err := http.Get(url) + if err != nil { + fmtf.Printf("Error fetching login info from %s: %v", url, err) + continue + } + defer resp.Body.Close() + + var result loginInfoResponse + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + fmtf.Printf("Error decoding response from %s: %v", url, err) + continue + } + + if result.Retcode == 0 && result.Status == "ok" { + fmtf.Printf("成功绑定机器人selfid[%v]onebot api baseURL[%v]", result.Data.UserID, baseURL) + baseURLMapMu.Lock() + useridstr := strconv.FormatInt(result.Data.UserID, 10) + baseURLMap[useridstr] = baseURL + baseURLMapMu.Unlock() + } + } +} + +// GetBaseURLByUserID 通过 user_id 获取 baseURL +func GetBaseURLByUserID(userID string) (string, bool) { + baseURLMapMu.Lock() + defer baseURLMapMu.Unlock() + url, exists := baseURLMap[userID] + return url, exists +} + func GenerateUUID() string { return uuid.New().String() } @@ -158,7 +209,7 @@ func ExtractEventDetails(eventData map[string]interface{}) (string, structs.Usag func SendGroupMessage(groupID int64, userID int64, message string, selfid string) error { //TODO: 用userid作为了echo,在ws收到回调信息的时候,加入到全局撤回数组,AddMessageID,实现撤回 - if selfid != "" { + if server.IsSelfIDExists(selfid) { // 创建消息结构体 msg := map[string]interface{}{ "action": "send_group_msg", @@ -173,8 +224,13 @@ func SendGroupMessage(groupID int64, userID int64, message string, selfid string // 发送消息 return server.SendMessageBySelfID(selfid, msg) } - // 获取基础URL - baseURL := config.GetHttpPath() // 假设config.getHttpPath()返回基础URL + var baseURL string + if len(config.GetHttpPaths()) > 0 { + baseURL, _ = GetBaseURLByUserID(selfid) + } else { + // 获取基础URL + baseURL = config.GetHttpPath() // 假设config.getHttpPath()返回基础URL + } // 构建完整的URL baseURL = baseURL + "/send_group_msg" @@ -255,7 +311,7 @@ func SendGroupMessage(groupID int64, userID int64, message string, selfid string func SendGroupMessageMdPromptKeyboard(groupID int64, userID int64, message string, selfid string, newmsg string, response string, promptstr string) error { //TODO: 用userid作为了echo,在ws收到回调信息的时候,加入到全局撤回数组,AddMessageID,实现反向ws连接时候的撤回 - if selfid != "" { + if server.IsSelfIDExists(selfid) { // 创建消息结构体 msg := map[string]interface{}{ "action": "send_group_msg", @@ -493,7 +549,7 @@ func SendGroupMessageMdPromptKeyboard(groupID int64, userID int64, message strin } func SendPrivateMessage(UserID int64, message string, selfid string) error { - if selfid != "" { + if server.IsSelfIDExists(selfid) { // 创建消息结构体 msg := map[string]interface{}{ "action": "send_private_msg", @@ -507,8 +563,13 @@ func SendPrivateMessage(UserID int64, message string, selfid string) error { // 发送消息 return server.SendMessageBySelfID(selfid, msg) } - // 获取基础URL - baseURL := config.GetHttpPath() // 假设config.getHttpPath()返回基础URL + var baseURL string + if len(config.GetHttpPaths()) > 0 { + baseURL, _ = GetBaseURLByUserID(selfid) + } else { + // 获取基础URL + baseURL = config.GetHttpPath() // 假设config.getHttpPath()返回基础URL + } // 构建完整的URL baseURL = baseURL + "/send_private_msg" @@ -587,7 +648,7 @@ func SendPrivateMessage(UserID int64, message string, selfid string) error { } func SendPrivateMessageRaw(UserID int64, message string, selfid string) error { - if selfid != "" { + if server.IsSelfIDExists(selfid) { // 创建消息结构体 msg := map[string]interface{}{ "action": "send_private_msg", @@ -601,8 +662,13 @@ func SendPrivateMessageRaw(UserID int64, message string, selfid string) error { // 发送消息 return server.SendMessageBySelfID(selfid, msg) } - // 获取基础URL - baseURL := config.GetHttpPath() // 假设config.getHttpPath()返回基础URL + var baseURL string + if len(config.GetHttpPaths()) > 0 { + baseURL, _ = GetBaseURLByUserID(selfid) + } else { + // 获取基础URL + baseURL = config.GetHttpPath() // 假设config.getHttpPath()返回基础URL + } // 构建完整的URL baseURL = baseURL + "/send_private_msg" From e2a3709e3f506e5ca2d43e5e674d4b3902e82797 Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Sun, 19 May 2024 14:54:47 +0800 Subject: [PATCH 07/30] Beta121 (#119) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 * beta117 * beta118 * beta119 * beta120 * beta121 --- readme.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/readme.md b/readme.md index f6750d1..df0318c 100644 --- a/readme.md +++ b/readme.md @@ -168,6 +168,16 @@ settings: GET /gensokyo?prompt=example&api=conversation_ernie ``` +支持端点列表:(需配置:allApi : true) +```golang +http.HandleFunc("/conversation_gpt", app.ChatHandlerChatgpt) + http.HandleFunc("/conversation_hunyuan", app.ChatHandlerHunyuan) + http.HandleFunc("/conversation_ernie", app.ChatHandlerErnie) + http.HandleFunc("/conversation_rwkv", app.ChatHandlerRwkv) + http.HandleFunc("/conversation_tyqw", app.ChatHandlerTyqw) + http.HandleFunc("/conversation_glm", app.ChatHandlerGlm) +``` + ## 请求 `/conversation` 端点 与 `/gensokyo` 类似,`/conversation` 端点支持附加 `prompt` 参数。 From 21a233dbeddaa86300f7177f0ad7b9a8b67c4741 Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Sun, 19 May 2024 23:14:44 +0800 Subject: [PATCH 08/30] Beta122 (#120) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 * beta117 * beta118 * beta119 * beta120 * beta121 * beta122 --- ...47\345\244\247\346\250\241\345\236\213.md" | 90 +++++++++++++++++ ...3-\345\233\275\351\231\205\347\257\207.md" | 49 ++++++++++ ...216\245one-api\346\225\231\347\250\213.md" | 39 ++++++++ ...45\346\234\272\345\231\250\344\272\272.md" | 98 +++++++++++++++++++ readme.md | 18 ++-- 5 files changed, 287 insertions(+), 7 deletions(-) create mode 100644 "docs/\344\270\255\347\272\247-\345\244\247\346\250\241\345\236\213api\351\205\215\347\275\256\347\244\272\344\276\213-\345\233\275\344\272\247\345\244\247\346\250\241\345\236\213.md" create mode 100644 "docs/\344\270\255\347\272\247-\345\244\247\346\250\241\345\236\213api\351\205\215\347\275\256\347\244\272\344\276\213-\345\233\275\351\231\205\347\257\207.md" create mode 100644 "docs/\344\270\255\347\272\247-\345\257\271\346\216\245one-api\346\225\231\347\250\213.md" create mode 100644 "docs/\345\210\235\351\230\266-\347\256\200\345\215\225\346\216\245\345\205\245\346\234\272\345\231\250\344\272\272.md" diff --git "a/docs/\344\270\255\347\272\247-\345\244\247\346\250\241\345\236\213api\351\205\215\347\275\256\347\244\272\344\276\213-\345\233\275\344\272\247\345\244\247\346\250\241\345\236\213.md" "b/docs/\344\270\255\347\272\247-\345\244\247\346\250\241\345\236\213api\351\205\215\347\275\256\347\244\272\344\276\213-\345\233\275\344\272\247\345\244\247\346\250\241\345\236\213.md" new file mode 100644 index 0000000..90bc014 --- /dev/null +++ "b/docs/\344\270\255\347\272\247-\345\244\247\346\250\241\345\236\213api\351\205\215\347\275\256\347\244\272\344\276\213-\345\233\275\344\272\247\345\244\247\346\250\241\345\236\213.md" @@ -0,0 +1,90 @@ +# 国产大模型配置实例 + +### 混元配置项 +配置混元模型需要以下参数: +```yaml +secretId: "xxx" +secretKey: "xxx" +region: "" +maxTokensHunyuan: 1024 +hunyuanType: 3 # 可选类型:0=高级版, 1=标准版std, 2=hunyuan-lite, 3=hunyuan-standard, 4=hunyuan-standard-256K, 5=hunyuan-pro +hunyuanStreamModeration: true # 启用流式内容审核 +topPHunyuan: 1.0 +temperatureHunyuan: 1.0 # 控制生成内容的随机性 +``` + +### 文心配置项 +配置文心模型时需要以下参数,这里涵盖了接口的调用以及特殊参数的设置: +```yaml +wenxinAccessToken: "xxx" +wenxinApiPath: "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/eb-instant" +wenxinEmbeddingUrl: "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/embeddings/embedding-v1" # 百度的embedding接口URL +maxTokenWenxin: 1024 +wenxinTopp: 0.7 # 控制输出文本的多样性,取值范围0.1~1.0,默认0.7 +wenxinPenaltyScore: 1.0 # 增加生成token的惩罚,以减少重复,值越大惩罚越重 +wenxinMaxOutputTokens: 100 # 模型最大输出token数,范围2~1024 +``` + +### RWKV 模型配置(rwkv runner) +```yaml +rwkvApiPath: "https://" # 符合RWKV标准的API地址 +rwkvMaxTokens: 100 +rwkvTemperature: 1.1 +rwkvTopP: 0.5 +rwkvPresencePenalty: 0.5 +rwkvFrequencyPenalty: 1.1 +rwkvPenaltyDecay: 0.99 +rwkvTopK: 25 +rwkvSseType: 0 +rwkvGlobalPenalty: false +rwkvStop: + - "\n\nUser" +rwkvUserName: "User" +rwkvAssistantName: "Assistant" +rwkvSystemName: "System" +rwkvPreSystem: false +``` + +### TYQW 模型配置 +```yaml +tyqwApiPath: "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation" +tyqwMaxTokens: 1500 +tyqwModel: "qwen-turbo" +tyqwApiKey: "sk-" +tyqwWorkspace: "" +tyqwTemperature: 0.85 +tyqwTopP: 0.9 +tyqwPresencePenalty: 0.2 +tyqwFrequencyPenalty: 0.2 +tyqwRepetitionPenalty: 1.1 +tyqwPenaltyDecay: 0.99 +tyqwTopK: 40 +tyqwSeed: 1234 +tyqwSseType: 1 +tyqwGlobalPenalty: false +tyqwStop: + - "\n\nUser" +tyqwUserName: "User" +tyqwAssistantName: "Assistant" +tyqwSystemName: "System" +tyqwPreSystem: false +tyqwEnableSearch: false +``` + +### GLM 模型配置 +```yaml +glmApiPath: "https://open.bigmodel.cn/api/paas/v4/chat/completions" +glmModel: "glm-3-turbo" +glmApiKey: ".xxx" +glmRequestID: "" +glmDoSample: true +glmTemperature: 0.95 +glmTopP: 0.9 +glmMaxTokens: 1024 +glmStop: + - "stop_token" +glmTools: + - "" +glmToolChoice: "auto" +glmUserID: "" +``` \ No newline at end of file diff --git "a/docs/\344\270\255\347\272\247-\345\244\247\346\250\241\345\236\213api\351\205\215\347\275\256\347\244\272\344\276\213-\345\233\275\351\231\205\347\257\207.md" "b/docs/\344\270\255\347\272\247-\345\244\247\346\250\241\345\236\213api\351\205\215\347\275\256\347\244\272\344\276\213-\345\233\275\351\231\205\347\257\207.md" new file mode 100644 index 0000000..a773693 --- /dev/null +++ "b/docs/\344\270\255\347\272\247-\345\244\247\346\250\241\345\236\213api\351\205\215\347\275\256\347\244\272\344\276\213-\345\233\275\351\231\205\347\257\207.md" @@ -0,0 +1,49 @@ +### 国际模型配置实例 + +#### api2d 配置项 +```yaml +gptModel: "gpt-3.5-turbo" +gptApiPath: "https://openai.api2d.net/v1/chat/completions" +gptToken: "fk207628-***********" +maxTokenGpt: 1024 +gptModeration: false # 额外走腾讯云检查安全,不合规直接拦截. 仅api2d支持 +gptSafeMode: false +gptSseType: 0 +``` + +#### openai 测试配置 +```yaml +gptModel: "gpt-3.5-turbo" +gptApiPath: "https://api.openai.com/v1/chat/completions" +gptToken: "sk_8*******" +maxTokenGpt: 1024 +gptModeration: false # 额外走腾讯云检查安全,不合规直接拦截. 仅api2d支持 +gptSafeMode: false +gptSseType: 0 +standardGptApi: true # 标准的gptApi, OpenAI 和 Groq 需要开启 +``` + +#### Groq 测试配置 +```yaml +gptModel: "llama3-70b-8192" +gptApiPath: "https://api.groq.com/openai/v1/chat/completions" +gptToken: "gsk_8*******" +maxTokenGpt: 1024 +gptModeration: false # 额外走腾讯云检查安全,不合规直接拦截. 仅api2d支持 +gptSafeMode: false +gptSseType: 0 +standardGptApi: true # 标准的gptApi, OpenAI 和 Groq 需要开启 +``` + +#### One-API 测试配置(该项目也支持国内多个大模型,具体请参考one-api接入教程) +[one-api](https://github.com/songquanpeng/one-api) +```yaml +gptModel: "chatglm_turbo" +gptApiPath: "http://127.0.0.1:3000/v1/chat/completions" +gptToken: "sk-d*****" +maxTokenGpt: 1024 +gptModeration: false # 额外走腾讯云检查安全,不合规直接拦截. 仅api2d支持 +gptSafeMode: false +gptSseType: 0 +standardGptApi: true # 标准的gptApi, OpenAI 和 Groq 需要开启 +``` \ No newline at end of file diff --git "a/docs/\344\270\255\347\272\247-\345\257\271\346\216\245one-api\346\225\231\347\250\213.md" "b/docs/\344\270\255\347\272\247-\345\257\271\346\216\245one-api\346\225\231\347\250\213.md" new file mode 100644 index 0000000..9345e4e --- /dev/null +++ "b/docs/\344\270\255\347\272\247-\345\257\271\346\216\245one-api\346\225\231\347\250\213.md" @@ -0,0 +1,39 @@ +### 开始使用 One-API对接gsk-llm + +**步骤 1: 下载 One-API** +- One-API 是一个轻便易用的项目,包含一个可执行文件,无需其他环境支持,且带有 Web UI。 +- 下载链接:[one-api](https://github.com/songquanpeng/one-api) + +**步骤 2: 运行 One-API** +- 执行下载的 One-API 可执行文件。 +- 在浏览器中打开 `http://localhost:3000/`。 + +**步骤 3: 登录** +- 使用默认用户名 `root` 和密码 `123456` 登录。 + +**步骤 4: 创建 API 渠道** +- 在网页控制台的顶栏选择“渠道”-“添加新的渠道”。 +- 为你的模型渠道命名,并在模型栏中输入你申请的模型名称,该栏支持自动补全。 +- 输入你从模型所在平台(如腾讯云、智谱、通义等)获取的 API access token。 +- 点击“提交”以创建 API 渠道。 + +**步骤 5: 生成令牌** +- 点击顶栏的“令牌”并创建一个新令牌。 +- 选择要使用的模型,创建令牌后点击绿色的“复制”按钮复制生成的令牌。 + +**步骤 6: 配置 gsk-llm** +- 在 gsk-llm 配置文件中更新以下配置以连接到你的 one-api 平台。 +```yaml +# One-API 测试配置 +gptModel: "chatglm_turbo" # 使用的模型名称 +gptApiPath: "http://127.0.0.1:3000/v1/chat/completions" # One-API 服务的端口号 +gptToken: "sk-dbmr0Oxxxxxxxxxxxxxxxxxxxxxxx" # 生成的密钥 +maxTokenGpt: 1024 +gptModeration: false +gptSafeMode: false +gptSseType: 0 +standardGptApi: true # 启用标准 GPT API +``` + +这样配置后,你就可以灵活地管理用量和使用的模型了。本项目的配置文件是热更新的,你不需要重启来应用配置. + diff --git "a/docs/\345\210\235\351\230\266-\347\256\200\345\215\225\346\216\245\345\205\245\346\234\272\345\231\250\344\272\272.md" "b/docs/\345\210\235\351\230\266-\347\256\200\345\215\225\346\216\245\345\205\245\346\234\272\345\231\250\344\272\272.md" new file mode 100644 index 0000000..7deaf70 --- /dev/null +++ "b/docs/\345\210\235\351\230\266-\347\256\200\345\215\225\346\216\245\345\205\245\346\234\272\345\231\250\344\272\272.md" @@ -0,0 +1,98 @@ + +# Gensokyo-LLM (gsk-llm) 初阶教程 + +## 名词介绍 +**gsk-llm**:在本教程中,我们将 Gensokyo-LLM 缩写为 **gsk-llm**。 + +**OneBot v11 实现端**:实现了 OneBot v11 标准的应用。在教程中使用 Gensokyo,任何采用 OneBot v11 的实现端均可成功对接使用。 + +## 支持实现端列表 +访问 [OneBot 生态系统](https://onebot.dev/ecosystem.html) 查看支持实现端的完整列表。 + +## 接入方法 + +### 使用ob11反向 WebSocket 接入 +1. 直接让机器人连接到以下本程序地址的 port 上: + - **反向链接地址**:`ws://127.0.0.1:46233` + - **Port**:46233 + +连接成功后,机器人便可收发信息。 + +### 使用ob11 HTTP API 接入方法 +1. 运行 gsk-llm 的 exe 可执行程序。 +2. 打开 `config.yml` 文件并进行以下设置: + - **通用配置项**: + ``` + allApi: true # 同时开启全部 API + useSse: true # 通过 stream 方式发送信息 + port: 46233 + path: "http://192.168.0.102:33800" + ``` + - **配置监听地址**:确保没有端口冲突,可以保持默认。 + - **配置 OneBot v11 的正向 HTTP API 地址**: + ``` + path: "http://127.0.0.1:46233/gensokyo" + ``` + - **上报消息地址**: + ``` + http://192.168.0.102:33800 + ``` + - **API 类型选择**: + ``` + apiType: 5 # 选择适合的 API 类型,文末对api类型有讲解 + ``` + - **API Key 设置**:填入对应平台的 API Key。 + +3. 设置 `systemPrompt` 为需要的人设提示词。 + +4. 根据注释完成其他必要配置。 + +一旦配置完成,你的机器人将作为一个基本的 AI 聊天对话机器人运行。 + +### 特别配置解释 +- **useSse: true**:API 通过 stream 方式发送信息,机器人将流式地发送信息。 +- **splitByPuntuations**: 40 #私聊截断率 +- **splitByPuntuationsGroup**: 40 #群聊截断率,仅在 SSE 时有效,100 表示每句截断。 + +建议截断率取值范围为 30-40,以便优化信息流的连贯性。 + +--- + +### API apiType 类型选择 + +在配置文件中,`apiType` 用于指定使用的 API 类型。您可以根据需要选择以下编号对应的 API 类型: + +- **0:混元** +- **1:文心** +- **2:GPT** +- **3:RWKV** +- **4:TYQW** +- **5:GLm** + +确保您有对应平台的 API key。 + +#### 特殊配置:API Type 2 + +当 `apiType` 设置为 **2(GPT)** 时,本程序支持兼容包括但不限于以下项目,以及所有采用 OpenAI 格式的 API 或中转项目: + +- [one-api](https://github.com/songquanpeng/one-api) +- [groq](https://console.groq.com) + +对于完全标准的 OpenAI 格式 API,调整配置如下: +- `standardGptApi: true` 表示启用原生的 OpenAI 格式 + +对于特殊的 OpenAI API 格式(api2d),则使用: +- `standardGptApi: false` + +API2d 提供了一些额外的配置,如安全模式和腾讯云审核等: +- [api-2d](https://api2d.com/) + +#### 网络代理设置 + +可以通过设置 `proxy: "http://192.168.0.113:7890"` 来使本程序通过网络代理。 + +#### 使用须知 + +请注意,部分平台如 QQ 开放平台和微信开放平台要求模型需要通过算法备案。请不要在这些平台使用未备案的模型。由此产生的服务号或机器人被下线的问题,本项目已尽到提醒的责任,概不负责。 + +--- \ No newline at end of file diff --git a/readme.md b/readme.md index df0318c..b54daad 100644 --- a/readme.md +++ b/readme.md @@ -10,6 +10,16 @@ _✨ 适用于Gensokyo以及Onebotv11的大模型一键端 ✨_ +--- +## 文档&教程 + +[初阶-简单接入机器人](/docs/初阶-简单接入机器人.md):简单将机器人接入QQ等6大平台 + +[中级-对接one-api教程](/docs/中级-对接one-api教程.md):可视化管理大模型api + +[中级-大模型api配置示例-国产大模型](/docs/中级-大模型api配置示例-国产大模型.md) + +[中级-大模型api配置示例-国际篇](/docs/中级-大模型api配置示例-国际篇.md) --- ## 特性 @@ -111,12 +121,6 @@ AhoCorasick算法实现的超高效文本IN-Out替换规则,可大量替换n --- -## 使用教程 - -施工中...(部署非常简单,有一定onebotv11机器人部署经验者可参考config.yml注释直接部署出来.) - ---- - # API接口调用说明 本文档提供了关于API接口的调用方法和配置文件的格式说明,帮助用户正确使用和配置。 @@ -170,7 +174,7 @@ GET /gensokyo?prompt=example&api=conversation_ernie 支持端点列表:(需配置:allApi : true) ```golang -http.HandleFunc("/conversation_gpt", app.ChatHandlerChatgpt) + http.HandleFunc("/conversation_gpt", app.ChatHandlerChatgpt) http.HandleFunc("/conversation_hunyuan", app.ChatHandlerHunyuan) http.HandleFunc("/conversation_ernie", app.ChatHandlerErnie) http.HandleFunc("/conversation_rwkv", app.ChatHandlerRwkv) From 8cc5b3d73c0ec10f60de74f9a878b485d113d2fc Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Sun, 19 May 2024 23:16:39 +0800 Subject: [PATCH 09/30] Update readme.md (#121) --- readme.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index b54daad..5db889d 100644 --- a/readme.md +++ b/readme.md @@ -20,6 +20,7 @@ _✨ 适用于Gensokyo以及Onebotv11的大模型一键端 ✨_ [中级-大模型api配置示例-国产大模型](/docs/中级-大模型api配置示例-国产大模型.md) [中级-大模型api配置示例-国际篇](/docs/中级-大模型api配置示例-国际篇.md) + --- ## 特性 @@ -462,4 +463,4 @@ GetAIPromptkeyboardPath可以是自身地址,可以带有prompt参数 3. **Inklewriter** - **项目主页**,[Ink on GitHub](https://github.com/inkle/ink). - - 它是一个允许用户创建交互式故事的网页平台。它设计了**ink**语言,它是一个开源项目,用于编写交互式叙述和游戏。 \ No newline at end of file + - 它是一个允许用户创建交互式故事的网页平台。它设计了**ink**语言,它是一个开源项目,用于编写交互式叙述和游戏。 From f567a821b27787a4963ef5416a37f07eadc1ebf7 Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Sun, 19 May 2024 23:21:41 +0800 Subject: [PATCH 10/30] Beta123 (#122) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 * beta117 * beta118 * beta119 * beta120 * beta121 * beta122 * beta123 --- ...\245\345\205\245\346\234\272\345\231\250\344\272\272.md" | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git "a/docs/\345\210\235\351\230\266-\347\256\200\345\215\225\346\216\245\345\205\245\346\234\272\345\231\250\344\272\272.md" "b/docs/\345\210\235\351\230\266-\347\256\200\345\215\225\346\216\245\345\205\245\346\234\272\345\231\250\344\272\272.md" index 7deaf70..783d281 100644 --- "a/docs/\345\210\235\351\230\266-\347\256\200\345\215\225\346\216\245\345\205\245\346\234\272\345\231\250\344\272\272.md" +++ "b/docs/\345\210\235\351\230\266-\347\256\200\345\215\225\346\216\245\345\205\245\346\234\272\345\231\250\344\272\272.md" @@ -29,13 +29,13 @@ path: "http://192.168.0.102:33800" ``` - **配置监听地址**:确保没有端口冲突,可以保持默认。 - - **配置 OneBot v11 的正向 HTTP API 地址**: + - **上报消息地址**: ``` path: "http://127.0.0.1:46233/gensokyo" ``` - - **上报消息地址**: + - **配置 OneBot v11 的正向 HTTP API 地址**: ``` - http://192.168.0.102:33800 + http://192.168.0.102:33800(该地址是onebotv11实现端的所在ip地址:http监听端口) ``` - **API 类型选择**: ``` From ea82b87f812fe3ed0af5ddcb09a2d74c24bb312f Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Sun, 19 May 2024 23:24:39 +0800 Subject: [PATCH 11/30] Beta124 (#123) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 * beta117 * beta118 * beta119 * beta120 * beta121 * beta122 * beta123 * beta124 --- ...16\245\345\205\245\346\234\272\345\231\250\344\272\272.md" | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git "a/docs/\345\210\235\351\230\266-\347\256\200\345\215\225\346\216\245\345\205\245\346\234\272\345\231\250\344\272\272.md" "b/docs/\345\210\235\351\230\266-\347\256\200\345\215\225\346\216\245\345\205\245\346\234\272\345\231\250\344\272\272.md" index 783d281..2ddd001 100644 --- "a/docs/\345\210\235\351\230\266-\347\256\200\345\215\225\346\216\245\345\205\245\346\234\272\345\231\250\344\272\272.md" +++ "b/docs/\345\210\235\351\230\266-\347\256\200\345\215\225\346\216\245\345\205\245\346\234\272\345\231\250\344\272\272.md" @@ -29,11 +29,11 @@ path: "http://192.168.0.102:33800" ``` - **配置监听地址**:确保没有端口冲突,可以保持默认。 - - **上报消息地址**: + - **上报消息地址**(在onebotv11实现端配置,指向gsk-llm): ``` path: "http://127.0.0.1:46233/gensokyo" ``` - - **配置 OneBot v11 的正向 HTTP API 地址**: + - **配置 OneBot v11 的正向 HTTP API 地址**(在gsk-llm配置,指向onebotv11实现端): ``` http://192.168.0.102:33800(该地址是onebotv11实现端的所在ip地址:http监听端口) ``` From 5414683ff14e47721afa685b20d89aa3139ccc7e Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Mon, 20 May 2024 00:50:41 +0800 Subject: [PATCH 12/30] Beta125 (#124) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 * beta117 * beta118 * beta119 * beta120 * beta121 * beta122 * beta123 * beta124 * beta125 --- applogic/chatglm.go | 2 +- applogic/gensokyo.go | 2 +- applogic/hunyuan.go | 46 ++++++++++++++++++++++++++++++++------------ config/config.go | 32 ++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 14 deletions(-) diff --git a/applogic/chatglm.go b/applogic/chatglm.go index 14b4aac..382f57a 100644 --- a/applogic/chatglm.go +++ b/applogic/chatglm.go @@ -59,7 +59,7 @@ func (app *App) ChatHandlerGlm(w http.ResponseWriter, r *http.Request) { // 读取URL参数 "userid" useridstr := r.URL.Query().Get("userid") - if promptstr != "" { + if useridstr != "" { // prompt 参数存在,可以根据需要进一步处理或记录 fmtf.Printf("Received userid parameter: %s\n", useridstr) } diff --git a/applogic/gensokyo.go b/applogic/gensokyo.go index eb5c435..9c320ed 100644 --- a/applogic/gensokyo.go +++ b/applogic/gensokyo.go @@ -558,7 +558,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } // glm会根据userid参数来封禁用户 - if config.GetApiType() == 5 { + if config.GetApiType() == 5 || basePath == "/conversation_glm" { urlParams.Add("userid", strconv.FormatInt(message.UserID, 10)) } diff --git a/applogic/hunyuan.go b/applogic/hunyuan.go index 2efd13a..a8d96ae 100644 --- a/applogic/hunyuan.go +++ b/applogic/hunyuan.go @@ -7,6 +7,7 @@ import ( "strings" "sync" + "github.com/google/uuid" "github.com/hoshinonyaruko/gensokyo-llm/config" "github.com/hoshinonyaruko/gensokyo-llm/fmtf" "github.com/hoshinonyaruko/gensokyo-llm/hunyuan" @@ -15,8 +16,12 @@ import ( "github.com/hoshinonyaruko/gensokyo-llm/utils" ) -var messageBuilder strings.Builder -var groupUserMessages sync.Map +var ( + messageBuilder strings.Builder + groupUserMessages sync.Map + mutexhunyuan sync.Mutex + lastCompleteResponseshunyuan sync.Map // 存储每个conversationId的完整累积信息 +) func (app *App) ChatHandlerHunyuan(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { @@ -655,10 +660,16 @@ func (app *App) ChatHandlerHunyuan(w http.ResponseWriter, r *http.Request) { return } - var responseTextBuilder strings.Builder - var totalUsage structs.UsageInfo + // 生成一个随机的UUID + randomUUID, err := uuid.NewRandom() + if err != nil { + http.Error(w, "Failed to generate UUID", http.StatusInternalServerError) + return + } + var totalUsage structs.UsageInfo // 有并发问题 for event := range response.BaseSSEResponse.Events { + if event.Err != nil { fmtf.Fprintf(w, "data: %s\n\n", fmtf.Sprintf("接收事件时发生错误: %v", event.Err)) flusher.Flush() @@ -673,13 +684,24 @@ func (app *App) ChatHandlerHunyuan(w http.ResponseWriter, r *http.Request) { continue } + // 在修改共享资源之前锁定Mutex + mutexhunyuan.Lock() + // 由于同一个上下文中 msg.ConversationID是相同的,而我们要区分更细粒度 所以添加UUID openai的api则设计了更细粒度的stramid,可以直接使用 + conversationId := msg.ConversationID + randomUUID.String() + // 读取完整信息 + completeResponse, _ := lastCompleteResponseshunyuan.LoadOrStore(conversationId, "") + // 提取出本次请求的响应 responseText, usageInfo := utils.ExtractEventDetails(eventData) - responseTextBuilder.WriteString(responseText) + // 更新存储的完整累积信息 + updatedCompleteResponse := completeResponse.(string) + responseText + lastCompleteResponseshunyuan.Store(conversationId, updatedCompleteResponse) + // 完成修改后解锁Mutex + mutexhunyuan.Unlock() + totalUsage.PromptTokens += usageInfo.PromptTokens totalUsage.CompletionTokens += usageInfo.CompletionTokens - // 发送当前事件的响应数据,但不包含assistantMessageID - //fmtf.Printf("发送当前事件的响应数据,但不包含assistantMessageID\n") + // 构建并发送当前事件的响应数据 tempResponseMap := map[string]interface{}{ "response": responseText, "conversationId": msg.ConversationID, @@ -693,12 +715,13 @@ func (app *App) ChatHandlerHunyuan(w http.ResponseWriter, r *http.Request) { } // 处理完所有事件后,生成并发送包含assistantMessageID的最终响应 - responseText := responseTextBuilder.String() - fmtf.Printf("处理完所有事件后,生成并发送包含assistantMessageID的最终响应:%v\n", responseText) + conversationId := msg.ConversationID + randomUUID.String() + completeResponse, _ := lastCompleteResponseshunyuan.LoadOrStore(conversationId, "") + fmtf.Printf("处理完所有事件后,生成并发送包含assistantMessageID的最终响应:%v\n", completeResponse.(string)) assistantMessageID, err := app.addMessage(structs.Message{ ConversationID: msg.ConversationID, ParentMessageID: userMessageID, - Text: responseText, + Text: completeResponse.(string), Role: "assistant", }) @@ -708,7 +731,7 @@ func (app *App) ChatHandlerHunyuan(w http.ResponseWriter, r *http.Request) { } finalResponseMap := map[string]interface{}{ - "response": responseText, + "response": completeResponse.(string), "conversationId": msg.ConversationID, "messageId": assistantMessageID, "details": map[string]interface{}{ @@ -720,7 +743,6 @@ func (app *App) ChatHandlerHunyuan(w http.ResponseWriter, r *http.Request) { flusher.Flush() } } - } func truncateHistoryHunYuan(history []structs.Message, prompt string, promptstr string) []structs.Message { diff --git a/config/config.go b/config/config.go index 5fda48c..cb2f2f9 100644 --- a/config/config.go +++ b/config/config.go @@ -450,6 +450,10 @@ func getGptTokenInternal(options ...string) string { return getGptTokenInternal() // 递归调用内部函数,不传递任何参数 } + if gptToken == "" { + return getGptTokenInternal() // 递归调用内部函数,不传递任何参数 + } + return gptToken } @@ -484,6 +488,10 @@ func getMaxTokenGptInternal(options ...string) int { return getMaxTokenGptInternal() // 递归调用内部函数,不传递任何参数 } + if maxTokenGpt == 0 { + return getMaxTokenGptInternal() // 递归调用内部函数,不传递任何参数 + } + return maxTokenGpt } @@ -2486,6 +2494,10 @@ func getGlmApiPathInternal(options ...string) string { return getGlmApiPathInternal() // 递归调用内部函数,不传递任何参数 } + if apiPath == "" { + return getGlmApiPathInternal() // 递归调用内部函数,不传递任何参数 + } + return apiPath } @@ -2520,6 +2532,10 @@ func getGlmModelInternal(options ...string) string { return getGlmModelInternal() // 递归调用内部函数,不传递任何参数 } + if model == "" { + return getGlmModelInternal() // 递归调用内部函数,不传递任何参数 + } + return model } @@ -2554,6 +2570,10 @@ func getGlmApiKeyInternal(options ...string) string { return getGlmApiKeyInternal() // 递归调用内部函数,不传递任何参数 } + if apiKey == "" { + return getGlmApiKeyInternal() // 递归调用内部函数,不传递任何参数 + } + return apiKey } @@ -2588,6 +2608,10 @@ func getGlmMaxTokensInternal(options ...string) int { return getGlmMaxTokensInternal() // 递归调用内部函数,不传递任何参数 } + if maxTokens == 0 { + return getGlmMaxTokensInternal() // 递归调用内部函数,不传递任何参数 + } + return maxTokens } @@ -2622,6 +2646,10 @@ func getGlmTemperatureInternal(options ...string) float64 { return getGlmTemperatureInternal() // 递归调用内部函数,不传递任何参数 } + if temperature == 0 { + return getGlmTemperatureInternal() // 递归调用内部函数,不传递任何参数 + } + return temperature } @@ -2696,6 +2724,10 @@ func getGlmTopPInternal(options ...string) float64 { return getGlmTopPInternal() // 递归调用内部函数,不传递任何参数 } + if topP == 0 { + return getGlmTopPInternal() // 递归调用内部函数,不传递任何参数 + } + return topP } From d3d10a2420219752f3fcc7ef98bba2d5e90986dc Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Mon, 20 May 2024 21:58:57 +0800 Subject: [PATCH 13/30] Beta126 (#125) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 * beta117 * beta118 * beta119 * beta120 * beta121 * beta122 * beta123 * beta124 * beta125 * beta126 --- applogic/chatgpt.go | 31 +++++++++++-------------- applogic/gensokyo.go | 49 ++++++++++++++++++++++++++++++++------- applogic/rwkv.go | 26 ++++++++++----------- applogic/tongyiqianwen.go | 28 +++++++++++----------- 4 files changed, 79 insertions(+), 55 deletions(-) diff --git a/applogic/chatgpt.go b/applogic/chatgpt.go index 2a242a4..9d1ba2d 100644 --- a/applogic/chatgpt.go +++ b/applogic/chatgpt.go @@ -425,6 +425,7 @@ func (app *App) ChatHandlerChatgpt(w http.ResponseWriter, r *http.Request) { mutexchatgpt.Lock() conversationId := eventData.ID // 假设conversationId从事件数据的ID字段获取 + // 储存eventData.ID 和 msg.ConversationID 对应关系 conversationMap.Store(msg.ConversationID, conversationId) //读取完整信息 completeResponse, _ := lastCompleteResponses.LoadOrStore(conversationId, "") @@ -466,11 +467,10 @@ func (app *App) ChatHandlerChatgpt(w http.ResponseWriter, r *http.Request) { if newContent != "" { tempResponseMap := map[string]interface{}{ "response": newContent, - "conversationId": conversationId, + "conversationId": msg.ConversationID, } tempResponseJSON, _ := json.Marshal(tempResponseMap) fmtf.Fprintf(w, "data: %s\n\n", string(tempResponseJSON)) - //fmt.Printf("测试返回:%v\n", string(tempResponseJSON)) flusher.Flush() } } @@ -493,24 +493,19 @@ func (app *App) ChatHandlerChatgpt(w http.ResponseWriter, r *http.Request) { } // 在所有事件处理完毕后发送最终响应 - // 首先从 conversationMap 获取真实的 conversationId - if actualConversationId, ok := conversationMap.Load(msg.ConversationID); ok { - if finalContent, ok := lastCompleteResponses.Load(actualConversationId); ok { - finalResponseMap := map[string]interface{}{ - "response": finalContent, - "conversationId": actualConversationId, - "messageId": assistantMessageID, - "details": map[string]interface{}{ - "usage": totalUsage, - }, - } - finalResponseJSON, _ := json.Marshal(finalResponseMap) - fmtf.Fprintf(w, "data: %s\n\n", string(finalResponseJSON)) - flusher.Flush() - } + finalResponseMap := map[string]interface{}{ + "response": completeResponse.(string), + "conversationId": msg.ConversationID, + "messageId": assistantMessageID, + "details": map[string]interface{}{ + "usage": totalUsage, + }, } - + finalResponseJSON, _ := json.Marshal(finalResponseMap) + fmtf.Fprintf(w, "data: %s\n\n", string(finalResponseJSON)) + flusher.Flush() } + } func truncateHistoryGpt(history []structs.Message, prompt string, promptstr string) []structs.Message { diff --git a/applogic/gensokyo.go b/applogic/gensokyo.go index 9c320ed..ad061c7 100644 --- a/applogic/gensokyo.go +++ b/applogic/gensokyo.go @@ -24,6 +24,15 @@ var newmsgToStringMap = make(map[string]string) var stringToIndexMap sync.Map var processMessageMu sync.Mutex +// UserInfo 结构体用于储存用户信息 +type UserInfo struct { + UserID int64 + GroupID int64 +} + +// globalMap 用于存储conversationID与UserInfo的映射 +var globalMap sync.Map + // RecordStringById 根据id记录一个string func RecordStringByNewmsg(id, value string) { newmsgToStringMap[id] = value @@ -464,6 +473,9 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // 请求conversation api 增加当前用户上下文 conversationID, parentMessageID, err := app.handleUserContext(message.UserID) + // 使用map映射conversationID和uid gid的关系 + StoreUserInfo(conversationID, message.UserID, message.GroupID) + //每句话清空上一句话的messageBuilder messageBuilder.Reset() fmtf.Printf("conversationID: %s,parentMessageID%s\n", conversationID, parentMessageID) @@ -893,7 +905,8 @@ func splitAndSendMessages(message structs.OnebotGroupMessage, line string, newme // 解析JSON数据 var sseData struct { - Response string `json:"response"` + Response string `json:"response"` + ConversationId string `json:"conversationId"` } err := json.Unmarshal([]byte(jsonStr), &sseData) if err != nil { @@ -903,13 +916,13 @@ func splitAndSendMessages(message structs.OnebotGroupMessage, line string, newme if sseData.Response != "\n\n" { // 处理提取出的信息 - processMessage(sseData.Response, message, newmesssage, selfid) + processMessage(sseData.Response, sseData.ConversationId, message, newmesssage, selfid) } else { fmtf.Printf("忽略llm末尾的换行符") } } -func processMessage(response string, msg structs.OnebotGroupMessage, newmesssage string, selfid string) { +func processMessage(response string, conversationid string, msg structs.OnebotGroupMessage, newmesssage string, selfid string) { key := utils.GetKey(msg.GroupID, msg.UserID) // 定义中文全角和英文标点符号 @@ -932,34 +945,36 @@ func processMessage(response string, msg structs.OnebotGroupMessage, newmesssage groupUserMessages.Store(key, value) processMessageMu.Unlock() // 完成更新后时解锁 + // 从conversation对应的sync map取出对应的用户和群号,避免高并发内容发送错乱 + userinfo, _ := GetUserInfo(conversationid) // 判断消息类型,如果是私人消息或私有群消息,发送私人消息;否则,根据配置决定是否发送群消息 if msg.RealMessageType == "group_private" || msg.MessageType == "private" { if !config.GetUsePrivateSSE() { - utils.SendPrivateMessage(msg.UserID, accumulatedMessage, selfid) + utils.SendPrivateMessage(userinfo.UserID, accumulatedMessage, selfid) } else { if IncrementIndex(newmesssage) == 1 { //第一条信息 //取出当前信息作为按钮回调 //CallbackData := GetStringById(lastMessageID) - uerid := strconv.FormatInt(msg.UserID, 10) + uerid := strconv.FormatInt(userinfo.UserID, 10) messageSSE := structs.InterfaceBody{ Content: accumulatedMessage, State: 1, ActionButton: 10, CallbackData: uerid, } - utils.SendPrivateMessageSSE(msg.UserID, messageSSE) + utils.SendPrivateMessageSSE(userinfo.UserID, messageSSE) } else { //SSE的前半部分 messageSSE := structs.InterfaceBody{ Content: accumulatedMessage, State: 1, } - utils.SendPrivateMessageSSE(msg.UserID, messageSSE) + utils.SendPrivateMessageSSE(userinfo.UserID, messageSSE) } } } else { - utils.SendGroupMessage(msg.GroupID, msg.UserID, accumulatedMessage, selfid) + utils.SendGroupMessage(userinfo.GroupID, userinfo.UserID, accumulatedMessage, selfid) } messageBuilder.Reset() // 重置消息构建器 @@ -993,3 +1008,21 @@ func handleWithdrawMessage(message structs.OnebotGroupMessage) { return } } + +// StoreUserInfo 用于存储用户信息到全局 map +func StoreUserInfo(conversationID string, userID int64, groupID int64) { + userInfo := UserInfo{ + UserID: userID, + GroupID: groupID, + } + globalMap.Store(conversationID, userInfo) +} + +// GetUserInfo 根据conversationID获取用户信息 +func GetUserInfo(conversationID string) (UserInfo, bool) { + value, ok := globalMap.Load(conversationID) + if ok { + return value.(UserInfo), true + } + return UserInfo{}, false +} diff --git a/applogic/rwkv.go b/applogic/rwkv.go index 543fd90..104b7b6 100644 --- a/applogic/rwkv.go +++ b/applogic/rwkv.go @@ -459,7 +459,7 @@ func (app *App) ChatHandlerRwkv(w http.ResponseWriter, r *http.Request) { if newContent != "" { tempResponseMap := map[string]interface{}{ "response": newContent, - "conversationId": conversationId, + "conversationId": msg.ConversationID, } tempResponseJSON, _ := json.Marshal(tempResponseMap) fmtf.Fprintf(w, "data: %s\n\n", string(tempResponseJSON)) @@ -485,20 +485,18 @@ func (app *App) ChatHandlerRwkv(w http.ResponseWriter, r *http.Request) { } // 在所有事件处理完毕后发送最终响应 - // 首先从 conversationMap 获取真实的 conversationId - if finalContent, ok := lastCompleteResponsesRwkv.Load(conversationId); ok { - finalResponseMap := map[string]interface{}{ - "response": finalContent, - "conversationId": conversationId, - "messageId": assistantMessageID, - "details": map[string]interface{}{ - "usage": totalUsage, - }, - } - finalResponseJSON, _ := json.Marshal(finalResponseMap) - fmtf.Fprintf(w, "data: %s\n\n", string(finalResponseJSON)) - flusher.Flush() + finalResponseMap := map[string]interface{}{ + "response": completeResponse.(string), + "conversationId": msg.ConversationID, + "messageId": assistantMessageID, + "details": map[string]interface{}{ + "usage": totalUsage, + }, } + finalResponseJSON, _ := json.Marshal(finalResponseMap) + fmtf.Fprintf(w, "data: %s\n\n", string(finalResponseJSON)) + flusher.Flush() + } } diff --git a/applogic/tongyiqianwen.go b/applogic/tongyiqianwen.go index 8277437..1197555 100644 --- a/applogic/tongyiqianwen.go +++ b/applogic/tongyiqianwen.go @@ -524,7 +524,7 @@ func (app *App) ChatHandlerTyqw(w http.ResponseWriter, r *http.Request) { if newContent != "" { tempResponseMap := map[string]interface{}{ "response": newContent, - "conversationId": conversationId, + "conversationId": msg.ConversationID, } tempResponseJSON, _ := json.Marshal(tempResponseMap) fmt.Fprintf(w, "data: %s\n\n", string(tempResponseJSON)) @@ -534,7 +534,7 @@ func (app *App) ChatHandlerTyqw(w http.ResponseWriter, r *http.Request) { } } - //一点点奇怪的转换 + // 一点点奇怪的转换 conversationId := msg.ConversationID + randomUUID.String() completeResponse, _ := lastCompleteResponsesTyqw.LoadOrStore(conversationId, "") // 在所有事件处理完毕后发送最终响应 @@ -551,20 +551,18 @@ func (app *App) ChatHandlerTyqw(w http.ResponseWriter, r *http.Request) { } // 在所有事件处理完毕后发送最终响应 - // 首先从 conversationMap 获取真实的 conversationId - if finalContent, ok := lastCompleteResponsesTyqw.Load(conversationId); ok { - finalResponseMap := map[string]interface{}{ - "response": finalContent, - "conversationId": conversationId, - "messageId": assistantMessageID, - "details": map[string]interface{}{ - "usage": totalUsage, - }, - } - finalResponseJSON, _ := json.Marshal(finalResponseMap) - fmtf.Fprintf(w, "data: %s\n\n", string(finalResponseJSON)) - flusher.Flush() + finalResponseMap := map[string]interface{}{ + "response": completeResponse.(string), + "conversationId": conversationId, + "messageId": assistantMessageID, + "details": map[string]interface{}{ + "usage": totalUsage, + }, } + finalResponseJSON, _ := json.Marshal(finalResponseMap) + fmtf.Fprintf(w, "data: %s\n\n", string(finalResponseJSON)) + flusher.Flush() + } } From ffa8ad3a7157ec5f929d2e00de30e39fb8441b4c Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Tue, 21 May 2024 00:05:04 +0800 Subject: [PATCH 14/30] Beta127 (#126) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 * beta117 * beta118 * beta119 * beta120 * beta121 * beta122 * beta123 * beta124 * beta125 * beta126 * beta127 --- applogic/gensokyo.go | 112 +++++++++++++++++++++++++++++-------------- applogic/hunyuan.go | 1 - 2 files changed, 75 insertions(+), 38 deletions(-) diff --git a/applogic/gensokyo.go b/applogic/gensokyo.go index ad061c7..1a6eedc 100644 --- a/applogic/gensokyo.go +++ b/applogic/gensokyo.go @@ -23,11 +23,14 @@ import ( var newmsgToStringMap = make(map[string]string) var stringToIndexMap sync.Map var processMessageMu sync.Mutex +var messages sync.Map // UserInfo 结构体用于储存用户信息 type UserInfo struct { - UserID int64 - GroupID int64 + UserID int64 + GroupID int64 + RealMessageType string + MessageType string } // globalMap 用于存储conversationID与UserInfo的映射 @@ -474,10 +477,10 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // 请求conversation api 增加当前用户上下文 conversationID, parentMessageID, err := app.handleUserContext(message.UserID) // 使用map映射conversationID和uid gid的关系 - StoreUserInfo(conversationID, message.UserID, message.GroupID) + StoreUserInfo(conversationID, message.UserID, message.GroupID, message.RealMessageType, message.MessageType) //每句话清空上一句话的messageBuilder - messageBuilder.Reset() + ClearMessage(conversationID) fmtf.Printf("conversationID: %s,parentMessageID%s\n", conversationID, parentMessageID) if err != nil { fmtf.Printf("Error handling user context: %v\n", err) @@ -624,9 +627,13 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { //接收到最后一条信息 if id, ok := responseData["messageId"].(string); ok { + conversationid := responseData["conversationId"].(string) + // 从conversation对应的sync map取出对应的用户和群号,避免高并发内容发送错乱 + userinfo, _ := GetUserInfo(conversationid) + lastMessageID = id // 更新lastMessageID // 检查是否有未发送的消息部分 - key := utils.GetKey(message.GroupID, message.UserID) + key := utils.GetKey(userinfo.GroupID, userinfo.UserID) accumulatedMessageInterface, exists := groupUserMessages.Load(key) var accumulatedMessage string if exists { @@ -645,9 +652,9 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { if newPart != "" { fmtf.Printf("A完整信息: %s,已发送信息:%s 新部分:%s\n", response, accumulatedMessage, newPart) // 判断消息类型,如果是私人消息或私有群消息,发送私人消息;否则,根据配置决定是否发送群消息 - if message.RealMessageType == "group_private" || message.MessageType == "private" { + if userinfo.RealMessageType == "group_private" || userinfo.MessageType == "private" { if !config.GetUsePrivateSSE() { - utils.SendPrivateMessage(message.UserID, newPart, selfid) + utils.SendPrivateMessage(userinfo.UserID, newPart, selfid) } else { //判断是否最后一条 var state int @@ -660,23 +667,23 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { Content: newPart, State: state, } - utils.SendPrivateMessageSSE(message.UserID, messageSSE) + utils.SendPrivateMessageSSE(userinfo.UserID, messageSSE) } } else { // 这里发送的是newPart api最后补充的部分 if !config.GetMdPromptKeyboardAtGroup() { // 如果没有 EnhancedAContent if EnhancedAContent == "" { - utils.SendGroupMessage(message.GroupID, message.UserID, newPart, selfid) + utils.SendGroupMessage(userinfo.GroupID, userinfo.UserID, newPart, selfid) } else { - utils.SendGroupMessage(message.GroupID, message.UserID, newPart+EnhancedAContent, selfid) + utils.SendGroupMessage(userinfo.GroupID, userinfo.UserID, newPart+EnhancedAContent, selfid) } } else { // 如果没有 EnhancedAContent if EnhancedAContent == "" { - utils.SendGroupMessageMdPromptKeyboard(message.GroupID, message.UserID, newPart, selfid, newmsg, response, promptstr) + utils.SendGroupMessageMdPromptKeyboard(userinfo.GroupID, userinfo.UserID, newPart, selfid, newmsg, response, promptstr) } else { - utils.SendGroupMessageMdPromptKeyboard(message.GroupID, message.UserID, newPart+EnhancedAContent, selfid, newmsg, response, promptstr) + utils.SendGroupMessageMdPromptKeyboard(userinfo.GroupID, userinfo.UserID, newPart+EnhancedAContent, selfid, newmsg, response, promptstr) } } } @@ -690,13 +697,13 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { fmtf.Printf("B完整信息: %s,已发送信息:%s", response, accumulatedMessage) if accumulatedMessage == "" { // 判断消息类型,如果是私人消息或私有群消息,发送私人消息;否则,根据配置决定是否发送群消息 - if message.RealMessageType == "group_private" || message.MessageType == "private" { + if userinfo.RealMessageType == "group_private" || userinfo.MessageType == "private" { if !config.GetUsePrivateSSE() { // 如果没有 EnhancedAContent if EnhancedAContent == "" { - utils.SendPrivateMessage(message.UserID, response, selfid) + utils.SendPrivateMessage(userinfo.UserID, response, selfid) } else { - utils.SendPrivateMessage(message.UserID, response+EnhancedAContent, selfid) + utils.SendPrivateMessage(userinfo.UserID, response+EnhancedAContent, selfid) } } else { //判断是否最后一条 @@ -710,22 +717,22 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { Content: response, State: state, } - utils.SendPrivateMessageSSE(message.UserID, messageSSE) + utils.SendPrivateMessageSSE(userinfo.UserID, messageSSE) } } else { if !config.GetMdPromptKeyboardAtGroup() { // 如果没有 EnhancedAContent if EnhancedAContent == "" { - utils.SendGroupMessage(message.GroupID, message.UserID, response, selfid) + utils.SendGroupMessage(userinfo.GroupID, userinfo.UserID, response, selfid) } else { - utils.SendGroupMessage(message.GroupID, message.UserID, response+EnhancedAContent, selfid) + utils.SendGroupMessage(userinfo.GroupID, userinfo.UserID, response+EnhancedAContent, selfid) } } else { // 如果没有 EnhancedAContent if EnhancedAContent == "" { - utils.SendGroupMessageMdPromptKeyboard(message.GroupID, message.UserID, response, selfid, newmsg, response, promptstr) + utils.SendGroupMessageMdPromptKeyboard(userinfo.GroupID, userinfo.UserID, response, selfid, newmsg, response, promptstr) } else { - utils.SendGroupMessageMdPromptKeyboard(message.GroupID, message.UserID, response+EnhancedAContent, selfid, newmsg, response, promptstr) + utils.SendGroupMessageMdPromptKeyboard(userinfo.GroupID, userinfo.UserID, response+EnhancedAContent, selfid, newmsg, response, promptstr) } } @@ -733,7 +740,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } } // 提示词 整体切换A - app.ProcessPromptMarks(message.UserID, response, &promptstr) + app.ProcessPromptMarks(userinfo.UserID, response, &promptstr) // 清空之前加入缓存 // 缓存省钱部分 这里默认不被覆盖,如果主配置开了缓存,始终缓存. if config.GetUseCache() { @@ -753,7 +760,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { if !config.GetHideExtraLogs() { fmtf.Printf("收到流数据,切割并发送信息: %s", string(line)) } - splitAndSendMessages(message, string(line), newmsg, selfid) + splitAndSendMessages(string(line), newmsg, selfid) } } } @@ -898,7 +905,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } -func splitAndSendMessages(message structs.OnebotGroupMessage, line string, newmesssage string, selfid string) { +func splitAndSendMessages(line string, newmesssage string, selfid string) { // 提取JSON部分 dataPrefix := "data: " jsonStr := strings.TrimPrefix(line, dataPrefix) @@ -916,24 +923,26 @@ func splitAndSendMessages(message structs.OnebotGroupMessage, line string, newme if sseData.Response != "\n\n" { // 处理提取出的信息 - processMessage(sseData.Response, sseData.ConversationId, message, newmesssage, selfid) + processMessage(sseData.Response, sseData.ConversationId, newmesssage, selfid) } else { fmtf.Printf("忽略llm末尾的换行符") } } -func processMessage(response string, conversationid string, msg structs.OnebotGroupMessage, newmesssage string, selfid string) { - key := utils.GetKey(msg.GroupID, msg.UserID) +func processMessage(response string, conversationid string, newmesssage string, selfid string) { + // 从conversation对应的sync map取出对应的用户和群号,避免高并发内容发送错乱 + userinfo, _ := GetUserInfo(conversationid) + key := utils.GetKey(userinfo.GroupID, userinfo.UserID) // 定义中文全角和英文标点符号 punctuations := []rune{'。', '!', '?', ',', ',', '.', '!', '?', '~'} for _, char := range response { - messageBuilder.WriteRune(char) - if utils.ContainsRune(punctuations, char, msg.GroupID) { + AppendRune(conversationid, char) + if utils.ContainsRune(punctuations, char, userinfo.GroupID) { // 达到标点符号,发送累积的整个消息 - if messageBuilder.Len() > 0 { - accumulatedMessage := messageBuilder.String() + if GetMessageLength(conversationid) > 0 { + accumulatedMessage, _ := GetCurrentMessage(conversationid) // 锁定 processMessageMu.Lock() // 从sync.map读取当前的value @@ -945,10 +954,8 @@ func processMessage(response string, conversationid string, msg structs.OnebotGr groupUserMessages.Store(key, value) processMessageMu.Unlock() // 完成更新后时解锁 - // 从conversation对应的sync map取出对应的用户和群号,避免高并发内容发送错乱 - userinfo, _ := GetUserInfo(conversationid) // 判断消息类型,如果是私人消息或私有群消息,发送私人消息;否则,根据配置决定是否发送群消息 - if msg.RealMessageType == "group_private" || msg.MessageType == "private" { + if userinfo.RealMessageType == "group_private" || userinfo.MessageType == "private" { if !config.GetUsePrivateSSE() { utils.SendPrivateMessage(userinfo.UserID, accumulatedMessage, selfid) } else { @@ -977,7 +984,7 @@ func processMessage(response string, conversationid string, msg structs.OnebotGr utils.SendGroupMessage(userinfo.GroupID, userinfo.UserID, accumulatedMessage, selfid) } - messageBuilder.Reset() // 重置消息构建器 + ClearMessage(conversationid) } } } @@ -1010,10 +1017,12 @@ func handleWithdrawMessage(message structs.OnebotGroupMessage) { } // StoreUserInfo 用于存储用户信息到全局 map -func StoreUserInfo(conversationID string, userID int64, groupID int64) { +func StoreUserInfo(conversationID string, userID int64, groupID int64, realMessageType string, messageType string) { userInfo := UserInfo{ - UserID: userID, - GroupID: groupID, + UserID: userID, + GroupID: groupID, + RealMessageType: realMessageType, + MessageType: messageType, } globalMap.Store(conversationID, userInfo) } @@ -1026,3 +1035,32 @@ func GetUserInfo(conversationID string) (UserInfo, bool) { } return UserInfo{}, false } + +func AppendRune(conversationID string, char rune) { + value, _ := messages.LoadOrStore(conversationID, "") + // 追加字符到现有字符串 + updatedValue := value.(string) + string(char) + messages.Store(conversationID, updatedValue) +} + +func GetCurrentMessage(conversationID string) (string, bool) { + value, ok := messages.Load(conversationID) + if ok { + return value.(string), true + } + return "", false +} + +func ClearMessage(conversationID string) { + messages.Delete(conversationID) +} + +func GetMessageLength(conversationID string) int { + value, ok := messages.Load(conversationID) + if ok { + // 断言字符串,返回长度 + return len(value.(string)) + } + // 如果没有找到对应的值,返回0 + return 0 +} diff --git a/applogic/hunyuan.go b/applogic/hunyuan.go index a8d96ae..913eadc 100644 --- a/applogic/hunyuan.go +++ b/applogic/hunyuan.go @@ -17,7 +17,6 @@ import ( ) var ( - messageBuilder strings.Builder groupUserMessages sync.Map mutexhunyuan sync.Mutex lastCompleteResponseshunyuan sync.Map // 存储每个conversationId的完整累积信息 From 9cae967b3614ac13b7d5e6ea7ebdc2b31d9a77c2 Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Thu, 23 May 2024 16:51:21 +0800 Subject: [PATCH 15/30] Beta128 (#127) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 * beta117 * beta118 * beta119 * beta120 * beta121 * beta122 * beta123 * beta124 * beta125 * beta126 * beta127 * beta128 --- applogic/chatglm.go | 6 +- applogic/chatgpt.go | 7 +- applogic/gensokyo.go | 4 +- applogic/rwkv.go | 6 +- applogic/tongyiqianwen.go | 6 +- applogic/yuanqi.go | 508 ++++++++++++++++++++++++++++++++++++ config/config.go | 175 +++++++++++++ main.go | 4 + structs/struct.go | 8 + structs/yuanqi.go | 26 ++ template/config_template.go | 8 +- 11 files changed, 743 insertions(+), 15 deletions(-) create mode 100644 applogic/yuanqi.go create mode 100644 structs/yuanqi.go diff --git a/applogic/chatglm.go b/applogic/chatglm.go index 382f57a..45eb34a 100644 --- a/applogic/chatglm.go +++ b/applogic/chatglm.go @@ -477,9 +477,9 @@ func truncateHistoryGlm(history []structs.Message, prompt string, promptstr stri } } - // 确保以user结尾,如果不是则尝试移除直到满足条件 - if len(history) > 0 && history[len(history)-1].Role != "user" { - for len(history) > 0 && history[len(history)-1].Role != "user" { + // 第三步:确保以assistant结尾 + if len(history) > 0 && history[len(history)-1].Role == "user" { + for len(history) > 0 && history[len(history)-1].Role != "assistant" { history = history[:len(history)-1] } } diff --git a/applogic/chatgpt.go b/applogic/chatgpt.go index 9d1ba2d..51e1513 100644 --- a/applogic/chatgpt.go +++ b/applogic/chatgpt.go @@ -515,6 +515,7 @@ func truncateHistoryGpt(history []structs.Message, prompt string, promptstr stri for _, msg := range history { tokenCount += len(msg.Text) } + fmt.Printf("测试:%v\n", history) if tokenCount >= MAX_TOKENS { // 第一步:从开始逐个移除消息,直到满足令牌数量限制 @@ -544,9 +545,9 @@ func truncateHistoryGpt(history []structs.Message, prompt string, promptstr stri } } - // 确保以user结尾,如果不是则尝试移除直到满足条件 - if len(history) > 0 && history[len(history)-1].Role != "user" { - for len(history) > 0 && history[len(history)-1].Role != "user" { + // 第三步:确保以assistant结尾 + if len(history) > 0 && history[len(history)-1].Role == "user" { + for len(history) > 0 && history[len(history)-1].Role != "assistant" { history = history[:len(history)-1] } } diff --git a/applogic/gensokyo.go b/applogic/gensokyo.go index 1a6eedc..e3c2e9a 100644 --- a/applogic/gensokyo.go +++ b/applogic/gensokyo.go @@ -572,8 +572,8 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { urlParams.Add("prompt", promptstr) } - // glm会根据userid参数来封禁用户 - if config.GetApiType() == 5 || basePath == "/conversation_glm" { + // 元器和glm会根据userid参数来自动封禁用户 + if config.GetApiType() == 5 || basePath == "/conversation_glm" || config.GetApiType() == 6 || basePath == "/conversation_yq" { urlParams.Add("userid", strconv.FormatInt(message.UserID, 10)) } diff --git a/applogic/rwkv.go b/applogic/rwkv.go index 104b7b6..ad205a2 100644 --- a/applogic/rwkv.go +++ b/applogic/rwkv.go @@ -537,9 +537,9 @@ func truncateHistoryRwkv(history []structs.Message, prompt string, promptstr str } } - // 确保以user结尾,如果不是则尝试移除直到满足条件 - if len(history) > 0 && history[len(history)-1].Role != "user" { - for len(history) > 0 && history[len(history)-1].Role != "user" { + // 第三步:确保以assistant结尾 + if len(history) > 0 && history[len(history)-1].Role == "user" { + for len(history) > 0 && history[len(history)-1].Role != "assistant" { history = history[:len(history)-1] } } diff --git a/applogic/tongyiqianwen.go b/applogic/tongyiqianwen.go index 1197555..b4bb6a1 100644 --- a/applogic/tongyiqianwen.go +++ b/applogic/tongyiqianwen.go @@ -603,9 +603,9 @@ func truncateHistoryTyqw(history []structs.Message, prompt string, promptstr str } } - // 确保以user结尾,如果不是则尝试移除直到满足条件 - if len(history) > 0 && history[len(history)-1].Role != "user" { - for len(history) > 0 && history[len(history)-1].Role != "user" { + // 第三步:确保以assistant结尾 + if len(history) > 0 && history[len(history)-1].Role == "user" { + for len(history) > 0 && history[len(history)-1].Role != "assistant" { history = history[:len(history)-1] } } diff --git a/applogic/yuanqi.go b/applogic/yuanqi.go new file mode 100644 index 0000000..542933c --- /dev/null +++ b/applogic/yuanqi.go @@ -0,0 +1,508 @@ +package applogic + +import ( + "bufio" + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "sync" + + "github.com/hoshinonyaruko/gensokyo-llm/config" + "github.com/hoshinonyaruko/gensokyo-llm/fmtf" + "github.com/hoshinonyaruko/gensokyo-llm/prompt" + "github.com/hoshinonyaruko/gensokyo-llm/structs" + "github.com/hoshinonyaruko/gensokyo-llm/utils" +) + +// 用于存储每个conversationId的最后一条消息内容 +var ( + // lastResponses 存储每个真实 conversationId 的最后响应文本 + lastResponsesYQ sync.Map + // conversationMap 存储 msg.ConversationID 到真实 conversationId 的映射 + conversationMapYQ sync.Map + lastCompleteResponsesYQ sync.Map // 存储每个conversationId的完整累积信息 + mutexyuanqi sync.Mutex +) + +func (app *App) ChatHandlerYuanQi(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + http.Error(w, "Only POST method is allowed", http.StatusMethodNotAllowed) + return + } + + // 获取访问者的IP地址 + ip := r.RemoteAddr // 注意:这可能包含端口号 + ip = strings.Split(ip, ":")[0] // 去除端口号,仅保留IP地址 + + // 获取IP白名单 + whiteList := config.IPWhiteList() + + // 检查IP是否在白名单中 + if !utils.Contains(whiteList, ip) { + http.Error(w, "Access denied", http.StatusInternalServerError) + return + } + + var msg structs.Message + err := json.NewDecoder(r.Body).Decode(&msg) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + // 读取URL参数 "prompt" + promptstr := r.URL.Query().Get("prompt") + if promptstr != "" { + // prompt 参数存在,可以根据需要进一步处理或记录 + fmtf.Printf("Received prompt parameter: %s\n", promptstr) + } + + // 读取URL参数 "userid" + useridstr := r.URL.Query().Get("userid") + if useridstr != "" { + // prompt 参数存在,可以根据需要进一步处理或记录 + fmtf.Printf("Received userid parameter: %s\n", useridstr) + } + + msg.Role = "user" + //颠倒用户输入 + if config.GetReverseUserPrompt() { + msg.Text = utils.ReverseString(msg.Text) + } + + if msg.ConversationID == "" { + msg.ConversationID = utils.GenerateUUID() + app.createConversation(msg.ConversationID) + } + + userMessageID, err := app.addMessage(msg) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + var history []structs.Message + + //根据是否有prompt参数 选择是否载入config.yml的prompt还是prompts文件夹的 + if promptstr == "" { + // 获取系统提示词 + systemPromptContent := config.SystemPrompt() + if systemPromptContent != "0" { + systemPrompt := structs.Message{ + Text: systemPromptContent, + Role: "system", + } + // 将系统提示词添加到历史信息的开始 + history = append([]structs.Message{systemPrompt}, history...) + } + + // 分别获取FirstQ&A, SecondQ&A, ThirdQ&A + pairs := []struct { + Q string + A string + RoleQ string // 问题的角色 + RoleA string // 答案的角色 + }{ + {config.GetFirstQ(), config.GetFirstA(), "user", "assistant"}, + {config.GetSecondQ(), config.GetSecondA(), "user", "assistant"}, + {config.GetThirdQ(), config.GetThirdA(), "user", "assistant"}, + } + + // 检查每一对Q&A是否均不为空,并追加到历史信息中 + for _, pair := range pairs { + if pair.Q != "" && pair.A != "" { + qMessage := structs.Message{ + Text: pair.Q, + Role: pair.RoleQ, + } + aMessage := structs.Message{ + Text: pair.A, + Role: pair.RoleA, + } + + // 注意追加的顺序,确保问题在答案之前 + history = append(history, qMessage, aMessage) + } + } + } else { + history, err = prompt.GetMessagesFromFilename(promptstr) + if err != nil { + fmtf.Printf("prompt.GetMessagesFromFilename error: %v\n", err) + } + } + + // 获取历史信息 + if msg.ParentMessageID != "" { + userhistory, err := app.getHistory(msg.ConversationID, msg.ParentMessageID) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // 截断历史信息 + userHistory := truncateHistoryGpt(userhistory, msg.Text, promptstr) + + if promptstr != "" { + // 注意追加的顺序,确保问题在系统提示词之后 + // 使用...操作符来展开userhistory切片并追加到history切片 + // 获取系统级预埋的系统自定义QA对 + systemHistory, err := prompt.GetMessagesExcludingSystem(promptstr) + if err != nil { + fmtf.Printf("Error getting system history: %v\n", err) + return + } + + // 处理增强QA逻辑 + if config.GetEnhancedQA(promptstr) { + // 确保系统历史与用户或助手历史数量一致,如果不足,则补足空的历史记录 + // 因为最后一个成员让给当前QA,所以-1 + if len(systemHistory)-2 > len(userHistory) { + difference := len(systemHistory) - len(userHistory) + for i := 0; i < difference; i++ { + userHistory = append(userHistory, structs.Message{Text: "", Role: "user"}) + userHistory = append(userHistory, structs.Message{Text: "", Role: "assistant"}) + } + } + + // 如果系统历史中只有一个成员,跳过覆盖逻辑,留给后续处理 + if len(systemHistory) > 1 { + // 将系统历史(除最后2个成员外)附加到相应的用户或助手历史上,采用倒序方式处理最近的记录 + for i := 0; i < len(systemHistory)-2; i++ { + sysMsg := systemHistory[i] + index := len(userHistory) - len(systemHistory) + i + if index >= 0 && index < len(userHistory) && (userHistory[index].Role == "user" || userHistory[index].Role == "assistant") { + userHistory[index].Text += fmt.Sprintf(" (%s)", sysMsg.Text) + } + } + } + } else { + // 将系统级别QA简单的附加在用户对话前方的位置(ai会知道,但不会主动引导) + history = append(history, systemHistory...) + } + + // 留下最后一个systemHistory成员进行后续处理 + } + + // 添加用户历史到总历史中 + history = append(history, userHistory...) + } + + apiURL := config.GetYuanqiApiPath(promptstr) + token := config.GetYuanqiToken() + fmtf.Printf("YuanQi上下文history:%v\n", history) + + // 构建请求到yuanqi API的请求体 + var requestBody structs.RequestDataYuanQi + + messages := make([]structs.MessageContent, 0, len(history)+1) + // 处理历史消息 + for _, hMsg := range history { + messageContent := structs.MessageContent{ + Role: hMsg.Role, + Content: []structs.ContentItem{{ + Type: "text", + Text: hMsg.Text, + }}, + } + messages = append(messages, messageContent) + } + + // 添加当前用户消息 + currentMessageContent := structs.MessageContent{ + Role: "user", + Content: []structs.ContentItem{{ + Type: "text", + Text: msg.Text, + }}, + } + messages = append(messages, currentMessageContent) + + // 创建请求数据结构体 + requestBody = structs.RequestDataYuanQi{ + AssistantID: config.GetYuanqiAssistantID(promptstr), + UserID: useridstr, + Stream: config.GetuseSse(promptstr), + ChatType: config.GetYuanqiChatType(promptstr), + Messages: messages, + } + + requestBodyJSON, _ := json.Marshal(requestBody) + + fmtf.Printf("yuanqi requestBody :%v", string(requestBodyJSON)) + + // 获取代理服务器地址 + proxyURL := config.GetProxy(promptstr) + if err != nil { + http.Error(w, fmtf.Sprintf("Failed to get proxy: %v", err), http.StatusInternalServerError) + return + } + + client := &http.Client{} + + // 检查是否有有效的代理地址 + if proxyURL != "" { + proxy, err := url.Parse(proxyURL) + if err != nil { + http.Error(w, fmtf.Sprintf("Failed to parse proxy URL: %v", err), http.StatusInternalServerError) + return + } + + // 配置客户端使用代理 + client.Transport = &http.Transport{ + Proxy: http.ProxyURL(proxy), + } + } + + // 创建HTTP请求 + req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(requestBodyJSON)) + if err != nil { + http.Error(w, fmtf.Sprintf("Failed to create request: %v", err), http.StatusInternalServerError) + return + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("X-Source", "openapi") + req.Header.Set("Authorization", fmtf.Sprintf("Bearer %s", token)) + + // 发送请求 + resp, err := client.Do(req) + if err != nil { + http.Error(w, fmtf.Sprintf("Error sending request to ChatGPT API: %v", err), http.StatusInternalServerError) + return + } + defer resp.Body.Close() + + if !config.GetuseSse(promptstr) { + // 处理响应 + responseBody, err := io.ReadAll(resp.Body) + if err != nil { + http.Error(w, fmtf.Sprintf("Failed to read response body: %v", err), http.StatusInternalServerError) + return + } + fmtf.Printf("yuanqi返回:%v", string(responseBody)) + // 假设已经成功发送请求并获得响应,responseBody是响应体的字节数据 + var apiResponse struct { + Choices []struct { + Message struct { + Content string `json:"content"` + } `json:"message"` + } `json:"choices"` + } + if err := json.Unmarshal(responseBody, &apiResponse); err != nil { + http.Error(w, fmtf.Sprintf("Error unmarshaling API response: %v", err), http.StatusInternalServerError) + return + } + + // 从API响应中获取回复文本 + responseText := "" + if len(apiResponse.Choices) > 0 { + responseText = apiResponse.Choices[0].Message.Content + } + + // 添加助理消息 + assistantMessageID, err := app.addMessage(structs.Message{ + ConversationID: msg.ConversationID, + ParentMessageID: userMessageID, + Text: responseText, + Role: "assistant", + }) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // 构造响应数据,包括回复文本、对话ID、消息ID,以及使用情况(用例中未计算,可根据需要添加) + responseMap := map[string]interface{}{ + "response": responseText, + "conversationId": msg.ConversationID, + "messageId": assistantMessageID, + // 在此实际使用情况中,应该有逻辑来填充totalUsage + // 此处仅为示例,根据实际情况来调整 + "details": map[string]interface{}{ + "usage": structs.UsageInfo{ + PromptTokens: 0, // 示例值,需要根据实际情况计算 + CompletionTokens: 0, // 示例值,需要根据实际情况计算 + }, + }, + } + + // 设置响应头部为JSON格式 + w.Header().Set("Content-Type", "application/json") + // 将响应数据编码为JSON并发送 + if err := json.NewEncoder(w).Encode(responseMap); err != nil { + http.Error(w, fmtf.Sprintf("Error encoding response: %v", err), http.StatusInternalServerError) + return + } + } else { + // 设置SSE相关的响应头部 + w.Header().Set("Content-Type", "text/event-stream") + w.Header().Set("Cache-Control", "no-cache") + w.Header().Set("Connection", "keep-alive") + + flusher, ok := w.(http.Flusher) + if !ok { + http.Error(w, "Streaming unsupported!", http.StatusInternalServerError) + return + } + + reader := bufio.NewReader(resp.Body) + var responseTextBuilder strings.Builder + var totalUsage structs.GPTUsageInfo + if config.GetGptSseType() == 1 { + for { + line, err := reader.ReadString('\n') + if err != nil { + if err == io.EOF { + break // 流结束 + } + // 处理错误 + fmtf.Fprintf(w, "data: %s\n\n", fmtf.Sprintf("读取流数据时发生错误: %v", err)) + flusher.Flush() + continue + } + + if strings.HasPrefix(line, "data: ") { + eventDataJSON := line[5:] // 去掉"data: "前缀 + + // 解析JSON数据 + var eventData structs.GPTEventData + if err := json.Unmarshal([]byte(eventDataJSON), &eventData); err != nil { + fmtf.Fprintf(w, "data: %s\n\n", fmtf.Sprintf("解析事件数据出错: %v", err)) + flusher.Flush() + continue + } + + // 遍历choices数组,累积所有文本内容 + for _, choice := range eventData.Choices { + responseTextBuilder.WriteString(choice.Delta.Content) + } + + // 如果存在需要发送的临时响应数据(例如,在事件流中间点) + // 注意:这里暂时省略了使用信息的处理,因为示例输出中没有包含这部分数据 + tempResponseMap := map[string]interface{}{ + "response": responseTextBuilder.String(), + "conversationId": msg.ConversationID, // 确保msg.ConversationID已经定义并初始化 + // "details" 字段留待进一步处理,如有必要 + } + tempResponseJSON, _ := json.Marshal(tempResponseMap) + fmtf.Fprintf(w, "data: %s\n\n", string(tempResponseJSON)) + flusher.Flush() + } + } + } else { + for { + line, err := reader.ReadString('\n') + if err != nil { + if err == io.EOF { + break // 流结束 + } + fmtf.Fprintf(w, "data: %s\n\n", fmtf.Sprintf("读取流数据时发生错误: %v", err)) + flusher.Flush() + continue + } + + if strings.HasPrefix(line, "data: ") { + eventDataJSON := line[5:] // 去掉"data: "前缀 + if eventDataJSON[1] != '{' { + fmtf.Println("非JSON数据,跳过:", eventDataJSON) + continue + } + + var eventData structs.GPTEventData + if err := json.Unmarshal([]byte(eventDataJSON), &eventData); err != nil { + fmtf.Fprintf(w, "data: %s\n\n", fmtf.Sprintf("解析事件数据出错: %v", err)) + flusher.Flush() + continue + } + + // 在修改共享资源之前锁定Mutex + mutexyuanqi.Lock() + + conversationId := eventData.ID // 假设conversationId从事件数据的ID字段获取 + // 储存eventData.ID 和 msg.ConversationID 对应关系 + conversationMapYQ.Store(msg.ConversationID, conversationId) + //读取完整信息 + completeResponse, _ := lastCompleteResponsesYQ.LoadOrStore(conversationId, "") + + // 检索上一次的响应文本 + lastResponse, _ := lastResponsesYQ.LoadOrStore(conversationId, "") + lastResponseText := lastResponse.(string) + + newContent := "" + for _, choice := range eventData.Choices { + // 如果新内容以旧内容开头 + if strings.HasPrefix(choice.Delta.Content, lastResponseText) { + // 特殊情况:当新内容和旧内容完全相同时,处理逻辑应当与新内容不以旧内容开头时相同 + if choice.Delta.Content == lastResponseText { + newContent += choice.Delta.Content + } else { + // 剔除旧内容部分,只保留新增的部分 + newContent += choice.Delta.Content[len(lastResponseText):] + } + } else { + // 如果新内容不以旧内容开头,可能是并发情况下的新消息,直接使用新内容 + newContent += choice.Delta.Content + } + } + + // 更新存储的完整累积信息 + updatedCompleteResponse := completeResponse.(string) + newContent + lastCompleteResponsesYQ.Store(conversationId, updatedCompleteResponse) + + // 使用累加的新内容更新存储的最后响应状态 + if newContent != "" { + lastResponsesYQ.Store(conversationId, newContent) + } + + // 完成修改后解锁Mutex + mutexyuanqi.Unlock() + + // 发送新增的内容 + if newContent != "" { + tempResponseMap := map[string]interface{}{ + "response": newContent, + "conversationId": msg.ConversationID, + } + tempResponseJSON, _ := json.Marshal(tempResponseMap) + fmtf.Fprintf(w, "data: %s\n\n", string(tempResponseJSON)) + flusher.Flush() + } + } + } + } + //一点点奇怪的转换 + conversationId, _ := conversationMapYQ.LoadOrStore(msg.ConversationID, "") + completeResponse, _ := lastCompleteResponsesYQ.LoadOrStore(conversationId, "") + // 在所有事件处理完毕后发送最终响应 + assistantMessageID, err := app.addMessage(structs.Message{ + ConversationID: msg.ConversationID, + ParentMessageID: userMessageID, + Text: completeResponse.(string), + Role: "assistant", + }) + + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // 在所有事件处理完毕后发送最终响应 + finalResponseMap := map[string]interface{}{ + "response": completeResponse.(string), + "conversationId": msg.ConversationID, + "messageId": assistantMessageID, + "details": map[string]interface{}{ + "usage": totalUsage, + }, + } + + finalResponseJSON, _ := json.Marshal(finalResponseMap) + fmtf.Fprintf(w, "data: %s\n\n", string(finalResponseJSON)) + flusher.Flush() + } + +} diff --git a/config/config.go b/config/config.go index cb2f2f9..51d4b0f 100644 --- a/config/config.go +++ b/config/config.go @@ -2880,3 +2880,178 @@ func getTemperatureHunyuanInternal(options ...string) float64 { return value } + +// 获取助手ID +func GetYuanqiAssistantID(options ...string) string { + mu.Lock() + defer mu.Unlock() + return getYuanqiAssistantIDInternal(options...) +} + +func getYuanqiAssistantIDInternal(options ...string) string { + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.YuanqiAssistantID + } + return "" // 默认值或错误处理 + } + + basename := options[0] + assistantIDInterface, err := prompt.GetSettingFromFilename(basename, "yuanqiAssistantID") + if err != nil { + log.Println("Error retrieving YuanqiAssistantID:", err) + return getYuanqiAssistantIDInternal() // 递归调用内部函数,不传递任何参数 + } + + assistantID, ok := assistantIDInterface.(string) + if !ok { // 检查类型断言是否失败 + log.Println("Type assertion failed for YuanqiAssistantID, fetching default") + return getYuanqiAssistantIDInternal() // 递归调用内部函数,不传递任何参数 + } + + if assistantID == "" { + return getYuanqiAssistantIDInternal() // 递归调用内部函数,不传递任何参数 + } + + return assistantID +} + +// 获取Token +func GetYuanqiToken(options ...string) string { + mu.Lock() + defer mu.Unlock() + return getYuanqiTokenInternal(options...) +} + +func getYuanqiTokenInternal(options ...string) string { + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.YuanqiToken + } + return "" // 默认值或错误处理 + } + + basename := options[0] + YuanqiTokenInterface, err := prompt.GetSettingFromFilename(basename, "yuanqiToken") + if err != nil { + log.Println("Error retrieving YuanqiToken:", err) + return getYuanqiTokenInternal() // 递归调用内部函数,不传递任何参数 + } + + YuanqiToken, ok := YuanqiTokenInterface.(string) + if !ok { // 检查类型断言是否失败 + log.Println("Type assertion failed for YuanqiToken, fetching default") + return getYuanqiTokenInternal() // 递归调用内部函数,不传递任何参数 + } + + if YuanqiToken == "" { + return getYuanqiTokenInternal() // 递归调用内部函数,不传递任何参数 + } + + return YuanqiToken +} + +// 获取助手版本 +func GetYuanqiVersion(options ...string) float64 { + mu.Lock() + defer mu.Unlock() + return getYuanqiVersionInternal(options...) +} + +func getYuanqiVersionInternal(options ...string) float64 { + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.YuanqiVersion + } + return 0.0 // 默认值或错误处理 + } + + basename := options[0] + versionInterface, err := prompt.GetSettingFromFilename(basename, "yuanqiVersion") + if err != nil { + log.Println("Error retrieving YuanqiVersion:", err) + return getYuanqiVersionInternal() // 递归调用内部函数,不传递任何参数 + } + + version, ok := versionInterface.(float64) + if !ok { // 检查类型断言是否失败 + log.Println("Type assertion failed for YuanqiVersion, fetching default") + return getYuanqiVersionInternal() // 递归调用内部函数,不传递任何参数 + } + + if version == 0 { + return getYuanqiVersionInternal() // 递归调用内部函数,不传递任何参数 + } + + return version +} + +// 获取聊天类型 +func GetYuanqiChatType(options ...string) string { + mu.Lock() + defer mu.Unlock() + return getYuanqiChatTypeInternal(options...) +} + +func getYuanqiChatTypeInternal(options ...string) string { + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.YuanqiChatType + } + return "published" // 默认值或错误处理 + } + + basename := options[0] + chatTypeInterface, err := prompt.GetSettingFromFilename(basename, "yuanqiChatType") + if err != nil { + log.Println("Error retrieving YuanqiChatType:", err) + return getYuanqiChatTypeInternal() // 递归调用内部函数,不传递任何参数 + } + + chatType, ok := chatTypeInterface.(string) + if !ok { // 检查类型断言是否失败 + log.Println("Type assertion failed for YuanqiChatType, fetching default") + return getYuanqiChatTypeInternal() // 递归调用内部函数,不传递任何参数 + } + + if chatType == "" { + return getYuanqiChatTypeInternal() // 递归调用内部函数,不传递任何参数 + } + + return chatType +} + +// 获取API地址 +func GetYuanqiApiPath(options ...string) string { + mu.Lock() + defer mu.Unlock() + return getYuanqiApiPathInternal(options...) +} + +func getYuanqiApiPathInternal(options ...string) string { + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.YuanqiApiPath + } + return "https://open.hunyuan.tencent.com/openapi/v1/agent/chat/completion" // 默认值或错误处理 + } + + basename := options[0] + chatTypeInterface, err := prompt.GetSettingFromFilename(basename, "yuanqiApiPath") + if err != nil { + log.Println("Error retrieving YuanqiApiPath:", err) + return getYuanqiApiPathInternal() // 递归调用内部函数,不传递任何参数 + } + + YuanqiApiPath, ok := chatTypeInterface.(string) + if !ok { // 检查类型断言是否失败 + log.Println("Type assertion failed for YuanqiApiPath, fetching default") + return getYuanqiApiPathInternal() // 递归调用内部函数,不传递任何参数 + } + + if YuanqiApiPath == "" { + return getYuanqiApiPathInternal() // 递归调用内部函数,不传递任何参数 + } + + return YuanqiApiPath +} diff --git a/main.go b/main.go index 7de4084..0ae4314 100644 --- a/main.go +++ b/main.go @@ -155,6 +155,9 @@ func main() { case 5: // 如果API类型是5,使用app.chatHandlerGlm http.HandleFunc("/conversation", app.ChatHandlerGlm) + case 6: + // 如果API类型是6,使用app.ChatHandlerYuanQi + http.HandleFunc("/conversation", app.ChatHandlerYuanQi) default: // 如果是其他值,可以选择一个默认的处理器或者记录一个错误 log.Printf("Unknown API type: %d", apiType) @@ -167,6 +170,7 @@ func main() { http.HandleFunc("/conversation_rwkv", app.ChatHandlerRwkv) http.HandleFunc("/conversation_tyqw", app.ChatHandlerTyqw) http.HandleFunc("/conversation_glm", app.ChatHandlerGlm) + http.HandleFunc("/conversation_yq", app.ChatHandlerYuanQi) } if config.GetSelfPath() != "" { rateLimiter := server.NewRateLimiter() diff --git a/structs/struct.go b/structs/struct.go index 3c9cebd..856598c 100644 --- a/structs/struct.go +++ b/structs/struct.go @@ -395,6 +395,14 @@ type Settings struct { GlmToolChoice string `yaml:"glmToolChoice"` // 工具选择策略 GlmUserID string `yaml:"glmUserID"` // 用户ID + YuanqiApiPath string `yaml:"yuanqiApiPath"` // 元器api地址 + YuanqiAssistantID string `yaml:"yuanqiAssistantID"` // 助手ID + YuanqiVersion float64 `yaml:"yuanqiVersion"` // 助手版本, 仅对内部开放 + YuanqiUserID string `yaml:"yuanqiUserID"` // 用户ID,调用者业务侧的用户ID,会影响智能体的数据统计,建议按实际情况填写 + YuanqiStream bool `yaml:"yuanqiStream"` // 是否启用流式返回,默认为false + YuanqiChatType string `yaml:"yuanqiChatType"` // 聊天类型,默认为published,preview时使用草稿态智能体,仅对内部开放 + YuanqiToken string `yaml:"yuanqiToken"` // Token + WSServerToken string `yaml:"wsServerToken"` WSPath string `yaml:"wsPath"` diff --git a/structs/yuanqi.go b/structs/yuanqi.go new file mode 100644 index 0000000..94d7144 --- /dev/null +++ b/structs/yuanqi.go @@ -0,0 +1,26 @@ +package structs + +type ContentItem struct { + Type string `json:"type,omitempty"` // "text" or "file_url" + Text string `json:"text,omitempty"` // 文本内容 + FileURL *FileURL `json:"file_url,omitempty"` // 文件内容 +} + +type FileURL struct { + Type string `json:"type"` // 文件类型: image, video, audio, pdf, doc, txt 等 + URL string `json:"url"` // 文件的URL地址 +} + +type MessageContent struct { + Role string `json:"role"` // "user" 或 "assistant" + Content []ContentItem `json:"content"` // 内容列表 +} + +type RequestDataYuanQi struct { + AssistantID string `json:"assistant_id"` // 助手ID + Version float64 `json:"version,omitempty"` // 助手版本 + UserID string `json:"user_id"` // 用户ID + Stream bool `json:"stream"` // 是否启用流式返回 + ChatType string `json:"chat_type,omitempty"` // 聊天类型 + Messages []MessageContent `json:"messages"` // 消息历史和当前消息 +} diff --git a/template/config_template.go b/template/config_template.go index d258476..9b27cb8 100644 --- a/template/config_template.go +++ b/template/config_template.go @@ -13,7 +13,7 @@ settings: paths : [] #当要连接多个onebotv11的http正向地址时,多个地址填入这里. lotus : "" #当填写另一个gensokyo-llm的http地址时,将请求另一个的conversation端点,实现多个llm不需要多次配置,简化配置,单独使用请忽略留空.例:http://192.168.0.1:12345(包含http头和端口) pathToken : "" #gensokyo正向http-api的access_token(是onebotv11标准的) - apiType : 0 #0=混元 1=文心(文心平台包含了N种模型...) 2=gpt 3=rwkv 4=通义千问 5=智谱AI + apiType : 0 #0=混元 1=文心(文心平台包含了N种模型...) 2=gpt 3=rwkv 4=通义千问 5=智谱AI 6=腾讯元器 iPWhiteList : ["192.168.0.102"] #接口调用,安全ip白名单,gensokyo的ip地址,或调用api的程序的ip地址 systemPrompt : [""] #人格提示词,或多个随机 firstQ : [""] #强化思想钢印,在每次对话的system之前固定一个QA,需都填写内容,会增加token消耗,可一定程度提高人格提示词效果,或抵抗催眠 @@ -184,6 +184,12 @@ settings: - "web_search" # 默认启用网络搜索工具 glmToolChoice: "auto" # 工具选择策略,目前支持auto,自动选择最合适的工具 glmUserID: "" # 用户唯一标识,用于跟踪和分析用户行为 + + # Yuanqi 助手配置文件,确保按业务需求配置。 + yuanqiApiPath: "https://open.hunyuan.tencent.com/openapi/v1/agent/chat/completions" + yuanqiAssistantID: "" # 助手ID,唯一标识您的助手实例 + yuanqiToken: "" # 元器Token + yuanqiChatType: "published" # 聊天类型,默认为published,支持preview模式下使用草稿态智能体 ` const Logo = ` From dfc18f70447ad5a6ee17dcb3f154efe7d4e84b73 Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Fri, 24 May 2024 14:35:46 +0800 Subject: [PATCH 16/30] Beta129 (#128) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 * beta117 * beta118 * beta119 * beta120 * beta121 * beta122 * beta123 * beta124 * beta125 * beta126 * beta127 * beta128 * beta129 --- applogic/yuanqi.go | 2 +- config/config.go | 12 ++++++------ prompt/prompt.go | 2 +- utils/utils.go | 5 +++++ 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/applogic/yuanqi.go b/applogic/yuanqi.go index 542933c..7b2f61c 100644 --- a/applogic/yuanqi.go +++ b/applogic/yuanqi.go @@ -192,7 +192,7 @@ func (app *App) ChatHandlerYuanQi(w http.ResponseWriter, r *http.Request) { } apiURL := config.GetYuanqiApiPath(promptstr) - token := config.GetYuanqiToken() + token := config.GetYuanqiToken(promptstr) fmtf.Printf("YuanQi上下文history:%v\n", history) // 构建请求到yuanqi API的请求体 diff --git a/config/config.go b/config/config.go index 51d4b0f..dff4434 100644 --- a/config/config.go +++ b/config/config.go @@ -65,7 +65,7 @@ func loadConfigFromFile(path string) (*Config, error) { conf := &Config{} if err := yaml.Unmarshal(configData, conf); err != nil { - log.Println("Failed to unmarshal YAML:", err) + log.Printf("failed to unmarshal YAML[%v]:%v", path, err) return nil, err } @@ -2897,7 +2897,7 @@ func getYuanqiAssistantIDInternal(options ...string) string { } basename := options[0] - assistantIDInterface, err := prompt.GetSettingFromFilename(basename, "yuanqiAssistantID") + assistantIDInterface, err := prompt.GetSettingFromFilename(basename, "YuanqiAssistantID") if err != nil { log.Println("Error retrieving YuanqiAssistantID:", err) return getYuanqiAssistantIDInternal() // 递归调用内部函数,不传递任何参数 @@ -2932,7 +2932,7 @@ func getYuanqiTokenInternal(options ...string) string { } basename := options[0] - YuanqiTokenInterface, err := prompt.GetSettingFromFilename(basename, "yuanqiToken") + YuanqiTokenInterface, err := prompt.GetSettingFromFilename(basename, "YuanqiToken") if err != nil { log.Println("Error retrieving YuanqiToken:", err) return getYuanqiTokenInternal() // 递归调用内部函数,不传递任何参数 @@ -2967,7 +2967,7 @@ func getYuanqiVersionInternal(options ...string) float64 { } basename := options[0] - versionInterface, err := prompt.GetSettingFromFilename(basename, "yuanqiVersion") + versionInterface, err := prompt.GetSettingFromFilename(basename, "YuanqiVersion") if err != nil { log.Println("Error retrieving YuanqiVersion:", err) return getYuanqiVersionInternal() // 递归调用内部函数,不传递任何参数 @@ -3002,7 +3002,7 @@ func getYuanqiChatTypeInternal(options ...string) string { } basename := options[0] - chatTypeInterface, err := prompt.GetSettingFromFilename(basename, "yuanqiChatType") + chatTypeInterface, err := prompt.GetSettingFromFilename(basename, "YuanqiChatType") if err != nil { log.Println("Error retrieving YuanqiChatType:", err) return getYuanqiChatTypeInternal() // 递归调用内部函数,不传递任何参数 @@ -3037,7 +3037,7 @@ func getYuanqiApiPathInternal(options ...string) string { } basename := options[0] - chatTypeInterface, err := prompt.GetSettingFromFilename(basename, "yuanqiApiPath") + chatTypeInterface, err := prompt.GetSettingFromFilename(basename, "YuanqiApiPath") if err != nil { log.Println("Error retrieving YuanqiApiPath:", err) return getYuanqiApiPathInternal() // 递归调用内部函数,不传递任何参数 diff --git a/prompt/prompt.go b/prompt/prompt.go index 1892f24..c87c51f 100644 --- a/prompt/prompt.go +++ b/prompt/prompt.go @@ -129,7 +129,7 @@ func loadFile(filename string) { var prompts PromptFile err = yaml.Unmarshal(data, &prompts) if err != nil { - log.Println("Failed to unmarshal YAML:", err) + log.Printf("failed to unmarshal YAML[%v]:%v", filename, err) return } diff --git a/utils/utils.go b/utils/utils.go index f543ef5..4c92a77 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -1119,6 +1119,11 @@ func SendSSEPrivateRestoreMessage(userID int64, RestoreResponse string) { // LanguageIntercept 检查文本语言,如果不在允许列表中,则返回 true 并发送消息 func LanguageIntercept(text string, message structs.OnebotGroupMessage, selfid string) bool { + hintWords := config.GetGroupHintWords() + // 遍历所有触发词,将其从文本中剔除 + for _, word := range hintWords { + text = strings.Replace(text, word, "", -1) + } info := whatlanggo.Detect(text) lang := whatlanggo.LangToString(info.Lang) fmtf.Printf("LanguageIntercept:%v\n", lang) From 38c3f7d20b9467c3cf163807712c877a3aed5109 Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Fri, 24 May 2024 14:54:55 +0800 Subject: [PATCH 17/30] Beta130 (#129) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 * beta117 * beta118 * beta119 * beta120 * beta121 * beta122 * beta123 * beta124 * beta125 * beta126 * beta127 * beta128 * beta129 * beta130 --- prompt/prompt.go | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/prompt/prompt.go b/prompt/prompt.go index c87c51f..b4b2ca4 100644 --- a/prompt/prompt.go +++ b/prompt/prompt.go @@ -9,7 +9,6 @@ import ( "reflect" "strings" "sync" - "time" "github.com/fsnotify/fsnotify" @@ -41,23 +40,6 @@ func init() { } } -// 防抖 -type FileLoader struct { - eventDelay time.Duration - lastLoad time.Time - fileName string -} - -func (fl *FileLoader) LoadFile(event fsnotify.Event) { - now := time.Now() - if now.Sub(fl.lastLoad) < fl.eventDelay { - return - } - fl.lastLoad = now - fl.fileName = event.Name - loadFile(event.Name) -} - // LoadPrompts 确保目录存在并尝试加载提示词文件 func LoadPrompts() error { // 构建目录路径 @@ -86,9 +68,6 @@ func LoadPrompts() error { return err } - // 添加一个100毫秒的Debouncing - fileLoader := &FileLoader{eventDelay: 100 * time.Millisecond} - go func() { for { select { @@ -97,7 +76,7 @@ func LoadPrompts() error { return } if event.Op&fsnotify.Write == fsnotify.Write { - fileLoader.LoadFile(event) + loadFile(event.Name) } case err, ok := <-watcher.Errors: if !ok { @@ -134,6 +113,11 @@ func loadFile(filename string) { } baseName := filepath.Base(filename) + if len(prompts.Prompts) == 0 { + fmt.Printf("prompts[%v][%v]载入中\n", baseName, filename) + return + } + promptsCache[baseName] = prompts fmt.Printf("成功载入prompts[%v]\n", baseName) } From da306c289239f0b8c1402f675e44d5a4e1e7262f Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Mon, 27 May 2024 17:46:49 +0800 Subject: [PATCH 18/30] Beta131 (#130) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 * beta117 * beta118 * beta119 * beta120 * beta121 * beta122 * beta123 * beta124 * beta125 * beta126 * beta127 * beta128 * beta129 * beta130 * beta131 --- applogic/gensokyo.go | 22 ++++++++++++++++++++-- template/config_template.go | 2 +- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/applogic/gensokyo.go b/applogic/gensokyo.go index e3c2e9a..43e48dd 100644 --- a/applogic/gensokyo.go +++ b/applogic/gensokyo.go @@ -228,6 +228,24 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { fmt.Printf("收到api参数: %s\n", api) } + // 从URL查询参数中获取skip_lang_check + skipLangCheckStr := r.URL.Query().Get("skip_lang_check") + + // 默认skipLangCheck为false + skipLangCheck := false + + if skipLangCheckStr != "" { + // 尝试将获取的字符串转换为布尔值 + var err error + skipLangCheck, err = strconv.ParseBool(skipLangCheckStr) + if err != nil { + // 如果转换出错,向客户端返回错误消息 + fmt.Fprintf(w, "Invalid skip_lang_check value: %s", skipLangCheckStr) + return + } + fmt.Printf("收到 skip_lang_check 参数: %v\n", skipLangCheck) + } + // 打印日志信息,包括prompt参数 fmtf.Printf("收到onebotv11信息: %+v\n", string(body)) @@ -335,8 +353,8 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } } - // 进行语言判断拦截 - if len(config.GetAllowedLanguages()) > 0 { + // 进行语言判断拦截 skipLangCheck为false时 + if len(config.GetAllowedLanguages()) > 0 && !skipLangCheck { if utils.LanguageIntercept(newmsg, message, selfid) { fmtf.Printf("不安全!不支持的语言,可在config.yml设置允许的语言,allowedLanguages配置项,Q: %v", newmsg) // 发送响应 diff --git a/template/config_template.go b/template/config_template.go index 9b27cb8..5d0be0e 100644 --- a/template/config_template.go +++ b/template/config_template.go @@ -61,7 +61,7 @@ settings: #] #语言过滤 - allowedLanguages : ["cmn"] #根据自身安全实力,酌情过滤,cmn代表中文,小写字母,[]空数组代表不限制. + allowedLanguages : ["cmn"] #根据自身安全实力,酌情过滤,cmn代表中文,小写字母,[]空数组代表不限制. /gensokyo api 可传参数skip_lang_check=true让某些信息跳过检查 langResponseMessages : ["抱歉,我不会**这个语言呢","我不会**这门语言,请使用中文和我对话吧"] #定型文,**会自动替换为检测到的语言 questionMaxLenth : 100 #最大问题字数. 0代表不限制 qmlResponseMessages : ["问题太长了,缩短问题试试吧"] #最大问题长度回复. From f7d60c1af9473cf099b070dd14f5fe7b9715de3d Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Mon, 27 May 2024 22:31:00 +0800 Subject: [PATCH 19/30] Beta132 (#131) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 * beta117 * beta118 * beta119 * beta120 * beta121 * beta122 * beta123 * beta124 * beta125 * beta126 * beta127 * beta128 * beta129 * beta130 * beta131 * beta132 --- applogic/yuanqi.go | 3 +++ utils/utils.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/applogic/yuanqi.go b/applogic/yuanqi.go index 7b2f61c..d9b04d5 100644 --- a/applogic/yuanqi.go +++ b/applogic/yuanqi.go @@ -221,6 +221,9 @@ func (app *App) ChatHandlerYuanQi(w http.ResponseWriter, r *http.Request) { } messages = append(messages, currentMessageContent) + // 保持QA顺序 即使用户发送多张图片 + messages = utils.MakeAlternating(messages) + // 创建请求数据结构体 requestBody = structs.RequestDataYuanQi{ AssistantID: config.GetYuanqiAssistantID(promptstr), diff --git a/utils/utils.go b/utils/utils.go index 4c92a77..9371903 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -1340,3 +1340,39 @@ func DeleteLatestMessage(messageType string, id int64, userid int64) error { // 发送删除消息请求 return sendDeleteRequest(u.String(), requestBodyBytes) } + +// MakeAlternating ensures that roles alternate between "user" and "assistant". +func MakeAlternating(messages []structs.MessageContent) []structs.MessageContent { + if len(messages) < 2 { + return messages // Not enough messages to need alternation or check + } + + // Initialize placeholders for the last seen user and assistant content + var lastUserContent, lastAssistantContent []structs.ContentItem + + correctedMessages := make([]structs.MessageContent, 0, len(messages)) + expectedRole := "user" // Start expecting "user" initially; this changes as we find roles + + for _, message := range messages { + if message.Role != expectedRole { + // If the current message does not match the expected role, insert the last seen content of the expected role + if expectedRole == "user" && lastUserContent != nil { + correctedMessages = append(correctedMessages, structs.MessageContent{Role: "user", Content: lastUserContent}) + } else if expectedRole == "assistant" && lastAssistantContent != nil { + correctedMessages = append(correctedMessages, structs.MessageContent{Role: "assistant", Content: lastAssistantContent}) + } + } + + // Append the current message and update last seen contents + correctedMessages = append(correctedMessages, message) + if message.Role == "user" { + lastUserContent = message.Content + expectedRole = "assistant" + } else if message.Role == "assistant" { + lastAssistantContent = message.Content + expectedRole = "user" + } + } + + return correctedMessages +} From 85d7fc442d6d55f5d90b05915166a620dc79f48d Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Tue, 28 May 2024 23:23:00 +0800 Subject: [PATCH 20/30] Beta133 (#132) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 * beta117 * beta118 * beta119 * beta120 * beta121 * beta122 * beta123 * beta124 * beta125 * beta126 * beta127 * beta128 * beta129 * beta130 * beta131 * beta132 * beta1333 --- applogic/yuanqi.go | 5 +- config/config.go | 142 ++++++++++++++++++++++++------------ structs/struct.go | 19 +++-- template/config_template.go | 10 ++- 4 files changed, 118 insertions(+), 58 deletions(-) diff --git a/applogic/yuanqi.go b/applogic/yuanqi.go index d9b04d5..6479149 100644 --- a/applogic/yuanqi.go +++ b/applogic/yuanqi.go @@ -192,7 +192,8 @@ func (app *App) ChatHandlerYuanQi(w http.ResponseWriter, r *http.Request) { } apiURL := config.GetYuanqiApiPath(promptstr) - token := config.GetYuanqiToken(promptstr) + + assistantID, token := config.GetYuanqiConf(promptstr) fmtf.Printf("YuanQi上下文history:%v\n", history) // 构建请求到yuanqi API的请求体 @@ -226,7 +227,7 @@ func (app *App) ChatHandlerYuanQi(w http.ResponseWriter, r *http.Request) { // 创建请求数据结构体 requestBody = structs.RequestDataYuanQi{ - AssistantID: config.GetYuanqiAssistantID(promptstr), + AssistantID: assistantID, UserID: useridstr, Stream: config.GetuseSse(promptstr), ChatType: config.GetYuanqiChatType(promptstr), diff --git a/config/config.go b/config/config.go index dff4434..ef2a01c 100644 --- a/config/config.go +++ b/config/config.go @@ -2881,74 +2881,122 @@ func getTemperatureHunyuanInternal(options ...string) float64 { return value } -// 获取助手ID -func GetYuanqiAssistantID(options ...string) string { - mu.Lock() - defer mu.Unlock() - return getYuanqiAssistantIDInternal(options...) -} +// // 获取助手ID +// func GetYuanqiAssistantID(options ...string) string { +// mu.Lock() +// defer mu.Unlock() +// return getYuanqiAssistantIDInternal(options...) +// } -func getYuanqiAssistantIDInternal(options ...string) string { - if len(options) == 0 || options[0] == "" { - if instance != nil { - return instance.Settings.YuanqiAssistantID - } - return "" // 默认值或错误处理 - } +// func getYuanqiAssistantIDInternal(options ...string) string { +// if len(options) == 0 || options[0] == "" { +// if instance != nil { +// return instance.Settings.YuanqiAssistantID +// } +// return "" // 默认值或错误处理 +// } - basename := options[0] - assistantIDInterface, err := prompt.GetSettingFromFilename(basename, "YuanqiAssistantID") - if err != nil { - log.Println("Error retrieving YuanqiAssistantID:", err) - return getYuanqiAssistantIDInternal() // 递归调用内部函数,不传递任何参数 - } +// basename := options[0] +// assistantIDInterface, err := prompt.GetSettingFromFilename(basename, "YuanqiAssistantID") +// if err != nil { +// log.Println("Error retrieving YuanqiAssistantID:", err) +// return getYuanqiAssistantIDInternal() // 递归调用内部函数,不传递任何参数 +// } - assistantID, ok := assistantIDInterface.(string) - if !ok { // 检查类型断言是否失败 - log.Println("Type assertion failed for YuanqiAssistantID, fetching default") - return getYuanqiAssistantIDInternal() // 递归调用内部函数,不传递任何参数 - } +// assistantID, ok := assistantIDInterface.(string) +// if !ok { // 检查类型断言是否失败 +// log.Println("Type assertion failed for YuanqiAssistantID, fetching default") +// return getYuanqiAssistantIDInternal() // 递归调用内部函数,不传递任何参数 +// } - if assistantID == "" { - return getYuanqiAssistantIDInternal() // 递归调用内部函数,不传递任何参数 - } +// if assistantID == "" { +// return getYuanqiAssistantIDInternal() // 递归调用内部函数,不传递任何参数 +// } - return assistantID -} +// return assistantID +// } + +// // 获取Token +// func GetYuanqiToken(options ...string) string { +// mu.Lock() +// defer mu.Unlock() +// return getYuanqiTokenInternal(options...) +// } + +// func getYuanqiTokenInternal(options ...string) string { +// if len(options) == 0 || options[0] == "" { +// if instance != nil { +// return instance.Settings.YuanqiToken +// } +// return "" // 默认值或错误处理 +// } + +// basename := options[0] +// YuanqiTokenInterface, err := prompt.GetSettingFromFilename(basename, "YuanqiToken") +// if err != nil { +// log.Println("Error retrieving YuanqiToken:", err) +// return getYuanqiTokenInternal() // 递归调用内部函数,不传递任何参数 +// } + +// YuanqiToken, ok := YuanqiTokenInterface.(string) +// if !ok { // 检查类型断言是否失败 +// log.Println("Type assertion failed for YuanqiToken, fetching default") +// return getYuanqiTokenInternal() // 递归调用内部函数,不传递任何参数 +// } -// 获取Token -func GetYuanqiToken(options ...string) string { +// if YuanqiToken == "" { +// return getYuanqiTokenInternal() // 递归调用内部函数,不传递任何参数 +// } + +// return YuanqiToken +// } + +// GetYuanqiConf return conf.YuanqiAssistantID, conf.YuanqiToken +func GetYuanqiConf(options ...string) (string, string) { mu.Lock() defer mu.Unlock() - return getYuanqiTokenInternal(options...) + return getYuanqiConfInternal(options...) } -func getYuanqiTokenInternal(options ...string) string { - if len(options) == 0 || options[0] == "" { - if instance != nil { - return instance.Settings.YuanqiToken +// getYuanqiConfInternal 内部递归函数,处理配置获取逻辑 +func getYuanqiConfInternal(options ...string) (string, string) { + // 从instance中读取配置数组 + if instance != nil && len(instance.Settings.Yuanqiconfs) > 0 { + if len(options) == 0 || options[0] == "" { + // 从instance全局变量中随机选择一个配置 + index := rand.Intn(len(instance.Settings.Yuanqiconfs)) + conf := instance.Settings.Yuanqiconfs[index] + return conf.YuanqiAssistantID, conf.YuanqiToken } - return "" // 默认值或错误处理 + } else { + log.Println("No configurations available in instance or default behavior") + return "", "" // 默认值或错误处理 } + // 使用prompt包从指定的文件名中获取配置 basename := options[0] - YuanqiTokenInterface, err := prompt.GetSettingFromFilename(basename, "YuanqiToken") + confInterface, err := prompt.GetSettingFromFilename(basename, "Yuanqiconfs") if err != nil { - log.Println("Error retrieving YuanqiToken:", err) - return getYuanqiTokenInternal() // 递归调用内部函数,不传递任何参数 + log.Printf("Error retrieving settings from file: %s, error: %v", basename, err) + return getYuanqiConfInternal() // 递归调用内部函数,不传递任何参数 } - YuanqiToken, ok := YuanqiTokenInterface.(string) - if !ok { // 检查类型断言是否失败 - log.Println("Type assertion failed for YuanqiToken, fetching default") - return getYuanqiTokenInternal() // 递归调用内部函数,不传递任何参数 + // 断言获取的interface{}为[]YuanqiConf + confs, ok := confInterface.([]structs.YuanqiConf) + if !ok { + log.Println("Type assertion failed for YuanqiConfs, attempting default behavior") + return getYuanqiConfInternal() // 递归调用内部函数,尝试默认配置 } - if YuanqiToken == "" { - return getYuanqiTokenInternal() // 递归调用内部函数,不传递任何参数 + if len(confs) == 0 { + log.Println("No configurations found in file:", basename) + return getYuanqiConfInternal() // 递归调用内部函数,尝试默认配置 } - return YuanqiToken + // 随机选择一个配置返回 + index := rand.Intn(len(confs)) + conf := confs[index] + return conf.YuanqiAssistantID, conf.YuanqiToken } // 获取助手版本 diff --git a/structs/struct.go b/structs/struct.go index 856598c..c88dff9 100644 --- a/structs/struct.go +++ b/structs/struct.go @@ -395,13 +395,12 @@ type Settings struct { GlmToolChoice string `yaml:"glmToolChoice"` // 工具选择策略 GlmUserID string `yaml:"glmUserID"` // 用户ID - YuanqiApiPath string `yaml:"yuanqiApiPath"` // 元器api地址 - YuanqiAssistantID string `yaml:"yuanqiAssistantID"` // 助手ID - YuanqiVersion float64 `yaml:"yuanqiVersion"` // 助手版本, 仅对内部开放 - YuanqiUserID string `yaml:"yuanqiUserID"` // 用户ID,调用者业务侧的用户ID,会影响智能体的数据统计,建议按实际情况填写 - YuanqiStream bool `yaml:"yuanqiStream"` // 是否启用流式返回,默认为false - YuanqiChatType string `yaml:"yuanqiChatType"` // 聊天类型,默认为published,preview时使用草稿态智能体,仅对内部开放 - YuanqiToken string `yaml:"yuanqiToken"` // Token + YuanqiApiPath string `yaml:"yuanqiApiPath"` // 元器api地址 + Yuanqiconfs []YuanqiConf `yaml:"yuanqiConfs"` // 元器api配置 支持多个 + YuanqiVersion float64 `yaml:"yuanqiVersion"` // 助手版本, 仅对内部开放 + YuanqiUserID string `yaml:"yuanqiUserID"` // 用户ID,调用者业务侧的用户ID,会影响智能体的数据统计,建议按实际情况填写 + YuanqiStream bool `yaml:"yuanqiStream"` // 是否启用流式返回,默认为false + YuanqiChatType string `yaml:"yuanqiChatType"` // 聊天类型,默认为published,preview时使用草稿态智能体,仅对内部开放 WSServerToken string `yaml:"wsServerToken"` WSPath string `yaml:"wsPath"` @@ -424,6 +423,12 @@ type Settings struct { PromptCoverA []string `yaml:"promptCoverA"` //暂时用不上 待实现 } +type YuanqiConf struct { + YuanqiAssistantID string `yaml:"yuanqiAssistantID"` // 助手ID + YuanqiToken string `yaml:"yuanqiToken"` // Token + YuanqiName string `yaml:"yuanqiName"` // 名字 +} + type MetaEvent struct { PostType string `json:"post_type"` MetaEventType string `json:"meta_event_type"` diff --git a/template/config_template.go b/template/config_template.go index 5d0be0e..35d1a88 100644 --- a/template/config_template.go +++ b/template/config_template.go @@ -187,9 +187,15 @@ settings: # Yuanqi 助手配置文件,确保按业务需求配置。 yuanqiApiPath: "https://open.hunyuan.tencent.com/openapi/v1/agent/chat/completions" - yuanqiAssistantID: "" # 助手ID,唯一标识您的助手实例 - yuanqiToken: "" # 元器Token yuanqiChatType: "published" # 聊天类型,默认为published,支持preview模式下使用草稿态智能体 + yuanqiConfs: + - yuanqiAssistantID: "123" + yuanqiToken: "123" + yuanqiName: "123" + - yuanqiAssistantID: "123" + uanqiToken: "123" + yuanqiName: "123" + ` const Logo = ` From 6c89588a008067cf62530177db306c26cc2b5473 Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Sat, 1 Jun 2024 18:31:14 +0800 Subject: [PATCH 21/30] Beta134 (#133) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 * beta117 * beta118 * beta119 * beta120 * beta121 * beta122 * beta123 * beta124 * beta125 * beta126 * beta127 * beta128 * beta129 * beta130 * beta131 * beta132 * beta1333 * beta134 --- applogic/app.go | 131 +++++++++++++++ applogic/chatgpt.go | 2 +- applogic/gensokyo.go | 99 +++++++++++- applogic/memory.go | 306 ++++++++++++++++++++++++++++++++++++ applogic/yuanqi.go | 50 +++++- config/config.go | 68 ++++++++ main.go | 6 + structs/struct.go | 11 ++ template/config_template.go | 5 + utils/utils.go | 110 +++++++++++++ 10 files changed, 779 insertions(+), 9 deletions(-) create mode 100644 applogic/memory.go diff --git a/applogic/app.go b/applogic/app.go index 32b8711..8cf56ad 100644 --- a/applogic/app.go +++ b/applogic/app.go @@ -288,6 +288,18 @@ func (app *App) updateUserContext(userID int64, parentMessageID string) error { return nil } +func (app *App) updateUserContextPro(userID int64, conversationID, parentMessageID string) error { + updateQuery := ` + UPDATE user_context + SET conversation_id = ?, parent_message_id = ? + WHERE user_id = ?;` + _, err := app.DB.Exec(updateQuery, conversationID, parentMessageID, userID) + if err != nil { + return fmt.Errorf("error updating user context: %w", err) + } + return nil +} + func (app *App) getHistory(conversationID, parentMessageID string) ([]structs.Message, error) { // 如果不开启上下文 if config.GetNoContext() { @@ -327,3 +339,122 @@ func (app *App) getHistory(conversationID, parentMessageID string) ([]structs.Me } return history, nil } + +// 记忆表 +func (app *App) EnsureUserMemoriesTableExists() error { + createTableSQL := ` + CREATE TABLE IF NOT EXISTS user_memories ( + memory_id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + conversation_id TEXT NOT NULL, + parent_message_id TEXT, + conversation_title TEXT NOT NULL + );` + + _, err := app.DB.Exec(createTableSQL) + if err != nil { + return fmt.Errorf("error creating user_memories table: %w", err) + } + + createUserIDIndexSQL := `CREATE INDEX IF NOT EXISTS idx_user_memories_user_id ON user_memories(user_id);` + _, err = app.DB.Exec(createUserIDIndexSQL) + if err != nil { + return fmt.Errorf("error creating index on user_memories(user_id): %w", err) + } + + createConvDetailsIndexSQL := `CREATE INDEX IF NOT EXISTS idx_user_memories_conversation_details ON user_memories(conversation_id, parent_message_id);` + _, err = app.DB.Exec(createConvDetailsIndexSQL) + if err != nil { + return fmt.Errorf("error creating index on user_memories(conversation_id, parent_message_id): %w", err) + } + + return nil +} + +func (app *App) AddUserMemory(userID int64, conversationID, parentMessageID, conversationTitle string) error { + // 插入新的记忆 + insertMemorySQL := ` + INSERT INTO user_memories (user_id, conversation_id, parent_message_id, conversation_title) + VALUES (?, ?, ?, ?);` + _, err := app.DB.Exec(insertMemorySQL, userID, conversationID, parentMessageID, conversationTitle) + if err != nil { + return fmt.Errorf("error inserting new memory: %w", err) + } + + // 检查并保持记忆数量不超过10条 + return app.ensureMemoryLimit(userID) +} + +func (app *App) updateConversationTitle(userID int64, conversationID, parentMessageID, newTitle string) error { + // 定义SQL更新语句 + updateQuery := ` + UPDATE user_memories + SET conversation_title = ? + WHERE user_id = ? AND conversation_id = ? AND parent_message_id = ?;` + + // 执行SQL更新操作 + _, err := app.DB.Exec(updateQuery, newTitle, userID, conversationID, parentMessageID) + if err != nil { + return fmt.Errorf("error updating conversation title: %w", err) + } + + return nil +} + +func (app *App) ensureMemoryLimit(userID int64) error { + // 查询当前记忆总数 + countQuerySQL := `SELECT COUNT(*) FROM user_memories WHERE user_id = ?;` + var count int + row := app.DB.QueryRow(countQuerySQL, userID) + err := row.Scan(&count) + if err != nil { + return fmt.Errorf("error counting memories: %w", err) + } + + // 如果记忆超过5条,则删除最旧的记忆 + if count > 5 { + deleteOldestMemorySQL := ` + DELETE FROM user_memories + WHERE memory_id IN ( + SELECT memory_id FROM user_memories + WHERE user_id = ? + ORDER BY memory_id ASC + LIMIT ? + );` + _, err := app.DB.Exec(deleteOldestMemorySQL, userID, count-5) + if err != nil { + return fmt.Errorf("error deleting old memories: %w", err) + } + } + + return nil +} + +func (app *App) GetUserMemories(userID int64) ([]structs.Memory, error) { + // 定义查询SQL,获取所有相关的记忆 + querySQL := ` + SELECT conversation_id, parent_message_id, conversation_title + FROM user_memories + WHERE user_id = ?; + ` + rows, err := app.DB.Query(querySQL, userID) + if err != nil { + return nil, fmt.Errorf("error querying user memories: %w", err) + } + defer rows.Close() // 确保关闭rows以释放数据库资源 + + var memories []structs.Memory + for rows.Next() { + var m structs.Memory + if err := rows.Scan(&m.ConversationID, &m.ParentMessageID, &m.ConversationTitle); err != nil { + return nil, fmt.Errorf("error scanning memory: %w", err) + } + memories = append(memories, m) + } + + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("error during rows iteration: %w", err) + } + + return memories, nil +} diff --git a/applogic/chatgpt.go b/applogic/chatgpt.go index 51e1513..0816252 100644 --- a/applogic/chatgpt.go +++ b/applogic/chatgpt.go @@ -515,7 +515,7 @@ func truncateHistoryGpt(history []structs.Message, prompt string, promptstr stri for _, msg := range history { tokenCount += len(msg.Text) } - fmt.Printf("测试:%v\n", history) + //fmt.Printf("测试:%v\n", history) if tokenCount >= MAX_TOKENS { // 第一步:从开始逐个移除消息,直到满足令牌数量限制 diff --git a/applogic/gensokyo.go b/applogic/gensokyo.go index 43e48dd..4db4ac4 100644 --- a/applogic/gensokyo.go +++ b/applogic/gensokyo.go @@ -136,7 +136,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // 判断是否是群聊 然后判断触发词 if message.RealMessageType != "group_private" && message.MessageType != "private" { - if !checkMessageForHints(message.RawMessage) { + if !checkMessageForHints(utils.RemoveBracketsContent(message.RawMessage)) { w.WriteHeader(http.StatusOK) w.Write([]byte("Group message not hint words.")) return @@ -497,6 +497,70 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // 使用map映射conversationID和uid gid的关系 StoreUserInfo(conversationID, message.UserID, message.GroupID, message.RealMessageType, message.MessageType) + // 保存记忆 + memoryCommand := config.GetMemoryCommand() + + // 检查checkResetCommand是否在memoryCommand列表中 + ismemoryCommand := false + for _, command := range memoryCommand { + if checkResetCommand == command { + ismemoryCommand = true + break + } + } + + // 处理保存记忆 + if ismemoryCommand { + app.handleSaveMemory(message, conversationID, parentMessageID) + return + } + + // 记忆列表 + memoryLoadCommand := config.GetMemoryLoadCommand() + + // 检查checkResetCommand是否在memoryLoadCommand列表中或以其为前缀 + ismemoryLoadCommand := false + isPrefixedMemoryLoadCommand := false // 新增变量用于检测前缀匹配 + for _, command := range memoryLoadCommand { + if checkResetCommand == command { + ismemoryLoadCommand = true + break + } + if strings.HasPrefix(checkResetCommand, command) { // 检查前缀 + isPrefixedMemoryLoadCommand = true + } + } + + // 处理记忆列表 + if ismemoryLoadCommand { + app.handleMemoryList(message) + return + } + + // 新增处理载入记忆的逻辑 + if isPrefixedMemoryLoadCommand { + app.handleLoadMemory(message, checkResetCommand) + return + } + + // 新对话 + newConversationCommand := config.GetNewConversationCommand() + + // 检查checkResetCommand是否在newConversationCommand列表中 + isnewConversationCommand := false + for _, command := range newConversationCommand { + if checkResetCommand == command { + isnewConversationCommand = true + break + } + } + + // 处理新对话 + if isnewConversationCommand { + app.handleNewConversation(message, conversationID, parentMessageID) + return + } + //每句话清空上一句话的messageBuilder ClearMessage(conversationID) fmtf.Printf("conversationID: %s,parentMessageID%s\n", conversationID, parentMessageID) @@ -822,15 +886,36 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // 添加第四个气泡 if config.GetNo4Promptkeyboard() { + // 合并所有命令到一个数组 + var allCommands []string + + // 获取并添加RestoreResponses RestoreResponses := config.GetRestoreCommand() - if len(RestoreResponses) > 0 { - selectedRestoreResponse := RestoreResponses[rand.Intn(len(RestoreResponses))] + allCommands = append(allCommands, RestoreResponses...) + + // 获取并添加memoryLoadCommand + memoryLoadCommand := config.GetMemoryLoadCommand() + allCommands = append(allCommands, memoryLoadCommand...) + + // 获取并添加memoryCommand + memoryCommand := config.GetMemoryCommand() + allCommands = append(allCommands, memoryCommand...) + + // 获取并添加newConversationCommand + newConversationCommand := config.GetNewConversationCommand() + allCommands = append(allCommands, newConversationCommand...) + + // 检查合并后的命令数组长度 + if len(allCommands) > 0 { + // 随机选择一个命令 + selectedCommand := allCommands[rand.Intn(len(allCommands))] + + // 在promptkeyboard的末尾添加选中的命令 if len(promptkeyboard) > 0 { - // 在promptkeyboard的末尾添加selectedRestoreResponse - promptkeyboard = append(promptkeyboard, selectedRestoreResponse) + promptkeyboard = append(promptkeyboard, selectedCommand) } else { - // 如果promptkeyboard为空,我们也应当初始化它,并添加选中的恢复命令 - promptkeyboard = []string{selectedRestoreResponse} + // 如果promptkeyboard为空,我们也应当初始化它,并添加选中的命令 + promptkeyboard = []string{selectedCommand} } } } diff --git a/applogic/memory.go b/applogic/memory.go new file mode 100644 index 0000000..3979203 --- /dev/null +++ b/applogic/memory.go @@ -0,0 +1,306 @@ +package applogic + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "net/url" + "strconv" + "strings" + "unicode" + + "github.com/hoshinonyaruko/gensokyo-llm/config" + "github.com/hoshinonyaruko/gensokyo-llm/fmtf" + "github.com/hoshinonyaruko/gensokyo-llm/structs" + "github.com/hoshinonyaruko/gensokyo-llm/utils" +) + +// 请你为我将我所输入的对话浓缩总结为一个标题 +func GetMemoryTitle(msg string) string { + baseurl := config.GetAIPromptkeyboardPath() + fmtf.Printf("获取到keyboard baseurl(复用):%v", baseurl) + // 使用net/url包来构建和编码URL + urlParams := url.Values{} + urlParams.Add("prompt", "memory") + + // 将查询参数编码后附加到基本URL上 + fullURL := baseurl + if len(urlParams) > 0 { + fullURL += "?" + urlParams.Encode() + } + + fmtf.Printf("Generated GetMemoryTitle URL:%v\n", fullURL) + + requestBody, err := json.Marshal(map[string]interface{}{ + "message": msg, + "conversationId": "", + "parentMessageId": "", + "user_id": "", + }) + + if err != nil { + fmt.Printf("Error marshalling request: %v\n", err) + return "默认标题" + } + + resp, err := http.Post(fullURL, "application/json", bytes.NewBuffer(requestBody)) + if err != nil { + fmt.Printf("Error sending request: %v\n", err) + return "默认标题" + } + defer resp.Body.Close() + + responseBody, err := io.ReadAll(resp.Body) + if err != nil { + fmt.Printf("Error reading response body: %v\n", err) + return "默认标题" + } + fmt.Printf("Response: %s\n", string(responseBody)) + + var responseData ResponseDataPromptKeyboard + if err := json.Unmarshal(responseBody, &responseData); err != nil { + fmt.Printf("Error unmarshalling response data: %v[%v]\n", err, string(responseBody)) + return "默认标题" + } + + // 预处理响应数据,移除可能的换行符 + preprocessedResponse := strings.TrimSpace(responseData.Response) + + // 去除所有标点符号和空格 + cleanedResponse := strings.Map(func(r rune) rune { + if unicode.IsPunct(r) || unicode.IsSpace(r) { + return -1 // 在 strings.Map 中,返回 -1 表示删除字符 + } + return r + }, preprocessedResponse) + + return cleanedResponse +} + +// 保存记忆 +func (app *App) handleSaveMemory(msg structs.OnebotGroupMessage, ConversationID string, ParentMessageID string) { + conversationTitle := "2024-5-19/18:26" // 默认标题,根据实际需求可能需要调整为动态生成的时间戳 + + // 添加用户记忆 + err := app.AddUserMemory(msg.UserID, ConversationID, ParentMessageID, conversationTitle) + if err != nil { + log.Printf("Error saving memory: %s", err) + return + } + + // 启动Go routine进行历史信息处理和标题更新 + go func() { + userHistory, err := app.getHistory(ConversationID, ParentMessageID) + if err != nil { + log.Printf("Error retrieving history: %s", err) + return + } + + // 处理历史信息为特定格式的字符串 + memoryTitle := formatHistory(userHistory) + newTitle := GetMemoryTitle(memoryTitle) // 获取最终的记忆标题 + + // 更新记忆标题 + err = app.updateConversationTitle(msg.UserID, ConversationID, ParentMessageID, newTitle) + if err != nil { + log.Printf("Error updating conversation title: %s", err) + } + }() + + var keyboard []string // 准备一个空的键盘数组 + // 获取记忆载入命令 + memoryLoadCommands := config.GetMemoryLoadCommand() + if len(memoryLoadCommands) > 0 { + keyboard = append(keyboard, memoryLoadCommands[0]) // 添加第一个命令到键盘数组 + } + + // 发送保存成功的响应 + saveMemoryResponse := "记忆保存成功!" + app.sendMemoryResponseWithkeyBoard(msg, saveMemoryResponse, keyboard) +} + +// 获取记忆列表 +func (app *App) handleMemoryList(msg structs.OnebotGroupMessage) { + memories, err := app.GetUserMemories(msg.UserID) + if err != nil { + log.Printf("Error retrieving memories: %s", err) + return + } + + // 组合格式化的文本 + var responseBuilder strings.Builder + responseBuilder.WriteString("当前记忆列表:\n") + + // 准备键盘数组,最多包含4个标题 + var keyboard []string + var loadMemoryCommand string + // 获取载入记忆指令 + memoryLoadCommands := config.GetMemoryLoadCommand() + if len(memoryLoadCommands) > 0 { + loadMemoryCommand = memoryLoadCommands[0] + } else { + loadMemoryCommand = "未设置载入指令" + } + + for _, memory := range memories { + responseBuilder.WriteString(memory.ConversationTitle + "\n") + // 更新键盘数组,确保最多只有4个元素 + if len(keyboard) >= 4 { + keyboard = keyboard[1:] // 移除最早的元素 + } + keyboard = append(keyboard, loadMemoryCommand+" "+memory.ConversationTitle) // 添加新的标题 + } + + var exampleTitle string + if len(memories) > 0 { + exampleTitle = string([]rune(memories[0].ConversationTitle)[:3]) + } + responseBuilder.WriteString(fmt.Sprintf("提示:发送 %s 任意标题开头的前n字即可载入记忆\n如:%s %s", loadMemoryCommand, loadMemoryCommand, exampleTitle)) + + // 发送组合后的信息,包括键盘数组 + app.sendMemoryResponseByline(msg, responseBuilder.String(), keyboard) +} + +// 载入记忆 +func (app *App) handleLoadMemory(msg structs.OnebotGroupMessage, checkResetCommand string) { + // 从配置获取载入记忆指令 + memoryLoadCommands := config.GetMemoryLoadCommand() + + // 移除所有载入记忆指令部分 + for _, command := range memoryLoadCommands { + checkResetCommand = strings.Replace(checkResetCommand, command, "", -1) + } + + // 移除空格得到匹配词 + matchTerm := strings.TrimSpace(checkResetCommand) + + // 获取用户记忆 + memories, err := app.GetUserMemories(msg.UserID) + if err != nil { + log.Printf("Error retrieving memories: %s", err) + app.sendMemoryResponse(msg, "获取记忆失败") + return + } + + // 查找匹配的记忆 + var matchedMemory *structs.Memory + for _, memory := range memories { + if strings.HasPrefix(memory.ConversationTitle, matchTerm) { + matchedMemory = &memory + break + } + } + + if matchedMemory == nil { + app.sendMemoryResponse(msg, "未找到匹配的记忆") + return + } + + // 载入记忆 + err = app.updateUserContextPro(msg.UserID, matchedMemory.ConversationID, matchedMemory.ParentMessageID) + if err != nil { + log.Printf("Error adding memory: %s", err) + app.sendMemoryResponse(msg, "载入记忆失败") + return + } + + // 组合回复信息 + responseMessage := fmt.Sprintf("成功载入了标题为 '%s' 的记忆", matchedMemory.ConversationTitle) + app.sendMemoryResponse(msg, responseMessage) +} + +func (app *App) sendMemoryResponseWithkeyBoard(msg structs.OnebotGroupMessage, response string, keyboard []string) { + strSelfID := strconv.FormatInt(msg.SelfID, 10) + if msg.RealMessageType == "group_private" || msg.MessageType == "private" { + if !config.GetUsePrivateSSE() { + utils.SendPrivateMessage(msg.UserID, response, strSelfID) + } else { + utils.SendSSEPrivateMessageWithKeyboard(msg.UserID, response, keyboard) + } + } else { + utils.SendGroupMessage(msg.GroupID, msg.UserID, response, strSelfID) + } +} + +func (app *App) sendMemoryResponse(msg structs.OnebotGroupMessage, response string) { + strSelfID := strconv.FormatInt(msg.SelfID, 10) + if msg.RealMessageType == "group_private" || msg.MessageType == "private" { + if !config.GetUsePrivateSSE() { + utils.SendPrivateMessage(msg.UserID, response, strSelfID) + } else { + utils.SendSSEPrivateMessage(msg.UserID, response) + } + } else { + utils.SendGroupMessage(msg.GroupID, msg.UserID, response, strSelfID) + } +} + +func (app *App) sendMemoryResponseByline(msg structs.OnebotGroupMessage, response string, keyboard []string) { + strSelfID := strconv.FormatInt(msg.SelfID, 10) + if msg.RealMessageType == "group_private" || msg.MessageType == "private" { + if !config.GetUsePrivateSSE() { + utils.SendPrivateMessage(msg.UserID, response, strSelfID) + } else { + utils.SendSSEPrivateMessageByLine(msg.UserID, response, keyboard) + } + } else { + utils.SendGroupMessage(msg.GroupID, msg.UserID, response, strSelfID) + } +} + +func formatHistory(history []structs.Message) string { + var result string + for _, message := range history { + rolePrefix := "Q:" + if message.Role == "answer" { + rolePrefix = "A:" + } + result += fmt.Sprintf("%s%s ", rolePrefix, message.Text) + } + return result +} + +func (app *App) handleNewConversation(msg structs.OnebotGroupMessage, conversationID string, parentMessageID string) { + // 使用预定义的时间戳作为会话标题 + conversationTitle := "2024-5-19/18:26" // 实际应用中应使用动态生成的时间戳 + + // 添加用户记忆 + err := app.AddUserMemory(msg.UserID, conversationID, parentMessageID, conversationTitle) + if err != nil { + log.Printf("Error saving memory: %s", err) + return + } + + // 启动Go routine进行历史信息处理和标题更新 + go func() { + userHistory, err := app.getHistory(conversationID, parentMessageID) + if err != nil { + log.Printf("Error retrieving history: %s", err) + return + } + + // 处理历史信息为特定格式的字符串 + memoryTitle := formatHistory(userHistory) + newTitle := GetMemoryTitle(memoryTitle) // 获取最终的记忆标题 + + // 更新记忆标题 + err = app.updateConversationTitle(msg.UserID, conversationID, parentMessageID, newTitle) + if err != nil { + log.Printf("Error updating conversation title: %s", err) + } + }() + + // 迁移用户到新的上下文 + app.migrateUserToNewContext(msg.UserID) + + // 获取并使用配置中指定的加载记忆指令 + loadCommand := config.GetMemoryLoadCommand() + if len(loadCommand) > 0 { + loadMemoryCommand := loadCommand[0] // 使用数组中的第一个指令 + saveMemoryResponse := fmt.Sprintf("旧的对话已经保存,可发送 %s 来查看,可以开始新的对话了!", loadMemoryCommand) + app.sendMemoryResponse(msg, saveMemoryResponse) + } +} diff --git a/applogic/yuanqi.go b/applogic/yuanqi.go index 6479149..f5dc2bf 100644 --- a/applogic/yuanqi.go +++ b/applogic/yuanqi.go @@ -144,7 +144,7 @@ func (app *App) ChatHandlerYuanQi(w http.ResponseWriter, r *http.Request) { } // 截断历史信息 - userHistory := truncateHistoryGpt(userhistory, msg.Text, promptstr) + userHistory := truncateHistoryYuanQi(userhistory, msg.Text, promptstr) if promptstr != "" { // 注意追加的顺序,确保问题在系统提示词之后 @@ -510,3 +510,51 @@ func (app *App) ChatHandlerYuanQi(w http.ResponseWriter, r *http.Request) { } } + +func truncateHistoryYuanQi(history []structs.Message, prompt string, promptstr string) []structs.Message { + MAX_TOKENS := config.GetYuanqiMaxToken(promptstr) + fmtf.Printf("测试,该用户最大上下文长度:%v\n", MAX_TOKENS) + fmtf.Printf("测试,该用户当前上下文:%v\n", history) + + tokenCount := len(prompt) + for _, msg := range history { + tokenCount += len(msg.Text) + } + + if tokenCount >= MAX_TOKENS { + // 第一步:从开始逐个移除消息,直到满足令牌数量限制 + for tokenCount > MAX_TOKENS && len(history) > 0 { + tokenCount -= len(history[0].Text) + history = history[1:] + + // 确保移除后,历史记录仍然以user消息结尾 + if len(history) > 0 && history[0].Role == "assistant" { + tokenCount -= len(history[0].Text) + history = history[1:] + } + } + } + + // 第二步:检查并移除包含空文本的QA对 + for i := 0; i < len(history)-1; i++ { // 使用len(history)-1是因为我们要检查成对的消息 + q := history[i] + a := history[i+1] + + // 检查Q和A是否成对,且A的角色应为assistant,Q的角色为user,避免删除非QA对的消息 + if q.Role == "user" && a.Role == "assistant" && (len(q.Text) == 0 || len(a.Text) == 0) { + fmtf.Println("closeai-找到了空的对话: ", q, a) + // 移除这对QA + history = append(history[:i], history[i+2:]...) + i-- // 因为删除了元素,调整索引以正确检查下一个元素 + } + } + + // 第三步:确保以assistant结尾 + if len(history) > 0 && history[len(history)-1].Role == "user" { + for len(history) > 0 && history[len(history)-1].Role != "assistant" { + history = history[:len(history)-1] + } + } + + return history +} diff --git a/config/config.go b/config/config.go index ef2a01c..173c44c 100644 --- a/config/config.go +++ b/config/config.go @@ -1132,6 +1132,36 @@ func GetWithdrawCommand() []string { return nil } +// 获取MemoryCommand +func GetMemoryCommand() []string { + mu.Lock() + defer mu.Unlock() + if instance != nil { + return instance.Settings.MemoryCommand + } + return nil +} + +// 获取MemoryLoadCommand +func GetMemoryLoadCommand() []string { + mu.Lock() + defer mu.Unlock() + if instance != nil { + return instance.Settings.MemoryLoadCommand + } + return nil +} + +// 获取NewConversationCommand +func GetNewConversationCommand() []string { + mu.Lock() + defer mu.Unlock() + if instance != nil { + return instance.Settings.NewConversationCommand + } + return nil +} + // 获取FunctionMode func GetFunctionMode() bool { mu.Lock() @@ -3103,3 +3133,41 @@ func getYuanqiApiPathInternal(options ...string) string { return YuanqiApiPath } + +// 获取YuanqiMaxToken +func GetYuanqiMaxToken(options ...string) int { + mu.Lock() + defer mu.Unlock() + return getYuanqiMaxTokenInternal(options...) +} + +// 内部逻辑执行函数,不处理锁,可以安全地递归调用 +func getYuanqiMaxTokenInternal(options ...string) int { + // 检查是否有参数传递进来,以及是否为空字符串 + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.YuanqiMaxToken + } + return 0 + } + + // 使用传入的 basename + basename := options[0] + YuanqiMaxTokenInterface, err := prompt.GetSettingFromFilename(basename, "YuanqiMaxToken") + if err != nil { + log.Println("Error retrieving MaxTokenYuanQi:", err) + return getYuanqiMaxTokenInternal() // 递归调用内部函数,不传递任何参数 + } + + YuanqiMaxToken, ok := YuanqiMaxTokenInterface.(int) + if !ok { // 检查是否断言失败 + fmt.Println("Type assertion failed for MaxTokenYuanQi, fetching default") + return getYuanqiMaxTokenInternal() // 递归调用内部函数,不传递任何参数 + } + + if YuanqiMaxToken == 0 { + return getYuanqiMaxTokenInternal() // 递归调用内部函数,不传递任何参数 + } + + return YuanqiMaxToken +} diff --git a/main.go b/main.go index 0ae4314..ce052ed 100644 --- a/main.go +++ b/main.go @@ -122,6 +122,12 @@ func main() { log.Fatalf("Failed to ensure CustomTableExist table exists: %v", err) } + // 用户多个记忆表 + err = app.EnsureUserMemoriesTableExists() + if err != nil { + log.Fatalf("Failed to ensure UserMemoriesTableExists table exists: %v", err) + } + // 加载 拦截词 err = app.ProcessSensitiveWords() if err != nil { diff --git a/structs/struct.go b/structs/struct.go index c88dff9..e90d480 100644 --- a/structs/struct.go +++ b/structs/struct.go @@ -334,6 +334,10 @@ type Settings struct { BlacklistResponseMessages []string `yaml:"blacklistResponseMessages"` NoContext bool `yaml:"noContext"` WithdrawCommand []string `yaml:"withdrawCommand"` + MemoryCommand []string `yaml:"memoryCommand"` + MemoryLoadCommand []string `yaml:"memoryLoadCommand"` + NewConversationCommand []string `yaml:"newConversationCommand"` + MemoryListMD bool `yaml:"memoryListMD"` FunctionMode bool `yaml:"functionMode"` FunctionPath string `yaml:"functionPath"` UseFunctionPromptkeyboard bool `yaml:"useFunctionPromptkeyboard"` @@ -401,6 +405,7 @@ type Settings struct { YuanqiUserID string `yaml:"yuanqiUserID"` // 用户ID,调用者业务侧的用户ID,会影响智能体的数据统计,建议按实际情况填写 YuanqiStream bool `yaml:"yuanqiStream"` // 是否启用流式返回,默认为false YuanqiChatType string `yaml:"yuanqiChatType"` // 聊天类型,默认为published,preview时使用草稿态智能体,仅对内部开放 + YuanqiMaxToken int `yaml:"yuanqiMaxToken"` // 内部控制的最大上下文对话截断 WSServerToken string `yaml:"wsServerToken"` WSPath string `yaml:"wsPath"` @@ -490,3 +495,9 @@ type CustomRecord struct { PromptStrStat int // New integer field for storing promptstr_stat Strs [10]string // Array to store str1 to str10 } + +type Memory struct { + ConversationID string + ParentMessageID string + ConversationTitle string +} diff --git a/template/config_template.go b/template/config_template.go index 35d1a88..051c26d 100644 --- a/template/config_template.go +++ b/template/config_template.go @@ -42,6 +42,10 @@ settings: savelogs : false #本地落地日志. noContext : false #不开启上下文 withdrawCommand : ["撤回"] #撤回指令 + memoryCommand : ["记忆"] #记忆指令 + memoryLoadCommand : ["载入"] #载入指令 + newConversationCommand : ["新对话"] #新对话指令 + memoryListMD : false #记忆列表使用md按钮(qq开放平台) hideExtraLogs : false #忽略流信息的log,提高性能 urlSendPics : false #自己构造图床加速图片发送.需配置公网ip+放通port+设置正确的selfPath groupHintWords : [] #当机器人位于群内时,需满足包含groupHintWords数组任意内容如[CQ:at,qq=2] 机器人的名字 等 @@ -188,6 +192,7 @@ settings: # Yuanqi 助手配置文件,确保按业务需求配置。 yuanqiApiPath: "https://open.hunyuan.tencent.com/openapi/v1/agent/chat/completions" yuanqiChatType: "published" # 聊天类型,默认为published,支持preview模式下使用草稿态智能体 + yuanqiMaxToken: 4096 yuanqiConfs: - yuanqiAssistantID: "123" yuanqiToken: "123" diff --git a/utils/utils.go b/utils/utils.go index 9371903..981b1e4 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -999,6 +999,116 @@ func SendSSEPrivateMessage(userID int64, content string) { } } +// SendSSEPrivateMessageWithKeyboard 分割并发送消息的核心逻辑,直接遍历字符串 +func SendSSEPrivateMessageWithKeyboard(userID int64, content string, keyboard []string) { + punctuations := []rune{'。', '!', '?', ',', ',', '.', '!', '?', '~'} + splitProbability := config.GetSplitByPuntuations() + + var parts []string + var currentPart strings.Builder + + for _, runeValue := range content { + currentPart.WriteRune(runeValue) + if strings.ContainsRune(string(punctuations), runeValue) { + // 根据概率决定是否分割 + if rand.Intn(100) < splitProbability { + parts = append(parts, currentPart.String()) + currentPart.Reset() + } + } + } + // 添加最后一部分(如果有的话) + if currentPart.Len() > 0 { + parts = append(parts, currentPart.String()) + } + + // 根据parts长度处理状态 + for i, part := range parts { + state := 1 + if i == len(parts)-2 { // 倒数第二部分 + state = 11 + } else if i == len(parts)-1 { // 最后一部分 + state = 20 + } + + // 构造消息体并发送 + messageSSE := structs.InterfaceBody{ + Content: part, + State: state, + } + + if state == 20 { // 对最后一部分特殊处理 + var promptKeyboard []string + if len(keyboard) == 0 { + RestoreResponses := config.GetRestoreCommand() + promptKeyboard = config.GetPromptkeyboard() + + if len(RestoreResponses) > 0 { + selectedRestoreResponse := RestoreResponses[rand.Intn(len(RestoreResponses))] + if len(promptKeyboard) > 0 { + promptKeyboard[0] = selectedRestoreResponse + } + } + } else { + promptKeyboard = keyboard + } + + messageSSE.PromptKeyboard = promptKeyboard + } + + // 发送SSE消息函数 + SendPrivateMessageSSE(userID, messageSSE) + } +} + +// SendSSEPrivateMessageByline 分割并发送消息的核心逻辑,直接遍历字符串 +func SendSSEPrivateMessageByLine(userID int64, content string, keyboard []string) { + // 直接使用 strings.Split 按行分割字符串 + parts := strings.Split(content, "\n") + + // 根据parts长度处理状态 + for i, part := range parts { + if part == "" { + continue // 跳过空行 + } + + state := 1 + if i == len(parts)-2 { // 倒数第二部分 + state = 11 + } else if i == len(parts)-1 { // 最后一部分 + state = 20 + } + + // 构造消息体并发送 + messageSSE := structs.InterfaceBody{ + Content: part + "\n", + State: state, + } + + if state == 20 { // 对最后一部分特殊处理 + var promptKeyboard []string + if len(keyboard) == 0 { + RestoreResponses := config.GetRestoreCommand() + promptKeyboard = config.GetPromptkeyboard() + + if len(RestoreResponses) > 0 { + selectedRestoreResponse := RestoreResponses[rand.Intn(len(RestoreResponses))] + if len(promptKeyboard) > 0 { + promptKeyboard[0] = selectedRestoreResponse + } + } + } else { + promptKeyboard = keyboard + } + + messageSSE.PromptKeyboard = promptKeyboard + } + + // 发送SSE消息函数 + SendPrivateMessageSSE(userID, messageSSE) + } +} + // SendSSEPrivateSafeMessage 分割并发送安全消息的核心逻辑,直接遍历字符串 func SendSSEPrivateSafeMessage(userID int64, saveresponse string) { // 将字符串转换为rune切片,以正确处理多字节字符 From 89745a25c56d2d3485dcd535ba2e6a781ea78db0 Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Mon, 3 Jun 2024 21:18:36 +0800 Subject: [PATCH 22/30] Beta135 (#134) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 * beta117 * beta118 * beta119 * beta120 * beta121 * beta122 * beta123 * beta124 * beta125 * beta126 * beta127 * beta128 * beta129 * beta130 * beta131 * beta132 * beta1333 * beta134 * beta135 --- applogic/gensokyo.go | 38 +++++++++++++++++++++++++++++-------- config/config.go | 10 ++++++++++ structs/struct.go | 1 + template/config_template.go | 1 + utils/utils.go | 6 +++--- 5 files changed, 45 insertions(+), 11 deletions(-) diff --git a/applogic/gensokyo.go b/applogic/gensokyo.go index 4db4ac4..a92b76e 100644 --- a/applogic/gensokyo.go +++ b/applogic/gensokyo.go @@ -492,8 +492,14 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } } - // 请求conversation api 增加当前用户上下文 - conversationID, parentMessageID, err := app.handleUserContext(message.UserID) + var conversationID, parentMessageID string + // 请求conversation api 增加当前群/用户上下文 + if config.GetGroupContext() && message.MessageType != "private" { + conversationID, parentMessageID, err = app.handleUserContext(message.GroupID) + } else { + conversationID, parentMessageID, err = app.handleUserContext(message.UserID) + } + // 使用map映射conversationID和uid gid的关系 StoreUserInfo(conversationID, message.UserID, message.GroupID, message.RealMessageType, message.MessageType) @@ -863,10 +869,18 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // 在SSE流结束后更新用户上下文 在这里调用gensokyo流式接口的最后一步 插推荐气泡 if lastMessageID != "" { fmtf.Printf("lastMessageID: %s\n", lastMessageID) - err := app.updateUserContext(message.UserID, lastMessageID) - if err != nil { - fmtf.Printf("Error updating user context: %v\n", err) + if config.GetGroupContext() && message.MessageType != "private" { + err := app.updateUserContext(message.GroupID, lastMessageID) + if err != nil { + fmtf.Printf("Error updating user context: %v\n", err) + } + } else { + err := app.updateUserContext(message.UserID, lastMessageID) + if err != nil { + fmtf.Printf("Error updating user context: %v\n", err) + } } + if message.RealMessageType == "group_private" || message.MessageType == "private" { if config.GetUsePrivateSSE() { @@ -960,10 +974,18 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // 更新用户上下文 if messageId, ok := responseData["messageId"].(string); ok { - err := app.updateUserContext(message.UserID, messageId) - if err != nil { - fmtf.Printf("Error updating user context: %v\n", err) + if config.GetGroupContext() && message.MessageType != "private" { + err := app.updateUserContext(message.GroupID, messageId) + if err != nil { + fmtf.Printf("Error updating user context: %v\n", err) + } + } else { + err := app.updateUserContext(message.UserID, messageId) + if err != nil { + fmtf.Printf("Error updating user context: %v\n", err) + } } + } } diff --git a/config/config.go b/config/config.go index 173c44c..1b9fff1 100644 --- a/config/config.go +++ b/config/config.go @@ -1122,6 +1122,16 @@ func GetNoContext() bool { return false } +// 获取GroupContext +func GetGroupContext() bool { + mu.Lock() + defer mu.Unlock() + if instance != nil { + return instance.Settings.GroupContext + } + return false +} + // 获取WithdrawCommand func GetWithdrawCommand() []string { mu.Lock() diff --git a/structs/struct.go b/structs/struct.go index e90d480..9fe5828 100644 --- a/structs/struct.go +++ b/structs/struct.go @@ -264,6 +264,7 @@ type Settings struct { UrlSendPics bool `yaml:"urlSendPics"` // 自己构造图床加速图片发送 MdPromptKeyboardAtGroup bool `yaml:"mdPromptKeyboardAtGroup"` // 群内使用md能力模拟PromptKeyboard GroupHintWords []string `yaml:"groupHintWords"` + GroupContext bool `yaml:"groupContext"` HunyuanType int `yaml:"hunyuanType"` MaxTokensHunyuan int `yaml:"maxTokensHunyuan"` diff --git a/template/config_template.go b/template/config_template.go index 051c26d..a8739a3 100644 --- a/template/config_template.go +++ b/template/config_template.go @@ -49,6 +49,7 @@ settings: hideExtraLogs : false #忽略流信息的log,提高性能 urlSendPics : false #自己构造图床加速图片发送.需配置公网ip+放通port+设置正确的selfPath groupHintWords : [] #当机器人位于群内时,需满足包含groupHintWords数组任意内容如[CQ:at,qq=2] 机器人的名字 等 + groupContext : false #群上下文 在智能体在群内时,以群为单位处理上下文. #Ws服务器配置 wsServerToken : "" #ws密钥 可以由onebotv11反向ws接入 diff --git a/utils/utils.go b/utils/utils.go index 981b1e4..85a59b9 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -981,11 +981,11 @@ func SendSSEPrivateMessage(userID int64, content string) { } if state == 20 { // 对最后一部分特殊处理 - RestoreResponses := config.GetRestoreCommand() + MemoryLoadCommand := config.GetMemoryLoadCommand() promptKeyboard := config.GetPromptkeyboard() - if len(RestoreResponses) > 0 { - selectedRestoreResponse := RestoreResponses[rand.Intn(len(RestoreResponses))] + if len(MemoryLoadCommand) > 0 { + selectedRestoreResponse := MemoryLoadCommand[rand.Intn(len(MemoryLoadCommand))] if len(promptKeyboard) > 0 { promptKeyboard[0] = selectedRestoreResponse } From 9473012173e60e80720b6aad18d323b6fe80db1b Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Mon, 3 Jun 2024 23:03:16 +0800 Subject: [PATCH 23/30] Beta136 (#135) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 * beta117 * beta118 * beta119 * beta120 * beta121 * beta122 * beta123 * beta124 * beta125 * beta126 * beta127 * beta128 * beta129 * beta130 * beta131 * beta132 * beta1333 * beta134 * beta135 * beta136 --- applogic/flow.go | 65 ++++++++++++++++++++------ applogic/gensokyo.go | 109 ++++++++++++++++++++++++++++++++----------- applogic/memory.go | 38 +++++++++++---- 3 files changed, 164 insertions(+), 48 deletions(-) diff --git a/applogic/flow.go b/applogic/flow.go index d402ace..605ff7b 100644 --- a/applogic/flow.go +++ b/applogic/flow.go @@ -14,6 +14,10 @@ import ( // ApplyPromptChoiceQ 应用promptchoiceQ的逻辑,动态修改requestmsg func (app *App) ApplyPromptChoiceQ(promptstr string, requestmsg *string, message *structs.OnebotGroupMessage) { + userid := message.UserID + if config.GetGroupContext() && message.MessageType != "private" { + userid = message.GroupID + } // 检查是否启用了EnhancedQA if config.GetEnhancedQA(promptstr) { @@ -35,7 +39,7 @@ func (app *App) ApplyPromptChoiceQ(promptstr string, requestmsg *string, message } else { var ischange bool // 获取用户剧情存档中的当前状态 - CustomRecord, err := app.FetchCustomRecord(message.UserID) + CustomRecord, err := app.FetchCustomRecord(userid) if err != nil { fmt.Printf("app.FetchCustomRecord 出错: %s\n", err) return @@ -133,6 +137,11 @@ func (app *App) ApplyPromptChoiceQ(promptstr string, requestmsg *string, message // ApplyPromptCoverQ 应用promptCoverQ的逻辑,动态覆盖requestmsg func (app *App) ApplyPromptCoverQ(promptstr string, requestmsg *string, message *structs.OnebotGroupMessage) { + userid := message.UserID + if config.GetGroupContext() && message.MessageType != "private" { + userid = message.GroupID + } + // 检查是否启用了EnhancedQA if config.GetEnhancedQA(promptstr) { promptCover := config.GetPromptCoverQ(promptstr) @@ -142,7 +151,7 @@ func (app *App) ApplyPromptCoverQ(promptstr string, requestmsg *string, message } else { var ischange bool // 获取用户剧情存档中的当前状态 - CustomRecord, err := app.FetchCustomRecord(message.UserID) + CustomRecord, err := app.FetchCustomRecord(userid) if err != nil { fmt.Printf("app.FetchCustomRecord 出错: %s\n", err) return @@ -240,11 +249,16 @@ func (app *App) ApplyPromptCoverQ(promptstr string, requestmsg *string, message // ApplySwitchOnQ 应用switchOnQ的逻辑,动态修改promptstr func (app *App) ApplySwitchOnQ(promptstr *string, requestmsg *string, message *structs.OnebotGroupMessage) { + userid := message.UserID + if config.GetGroupContext() && message.MessageType != "private" { + userid = message.GroupID + } + // promptstr 随 switchOnQ 变化 promptstrChoices := config.GetSwitchOnQ(*promptstr) if len(promptstrChoices) != 0 { // 获取用户剧情存档中的当前状态 - CustomRecord, err := app.FetchCustomRecord(message.UserID) + CustomRecord, err := app.FetchCustomRecord(userid) if err != nil { fmt.Printf("app.FetchCustomRecord 出错: %s\n", err) return @@ -298,7 +312,7 @@ func (app *App) ApplySwitchOnQ(promptstr *string, requestmsg *string, message *s *promptstr = bestText // 获取新的信号长度 刷新持久化数据库 PromptMarksLength := config.GetPromptMarksLength(*promptstr) - app.InsertCustomTableRecord(message.UserID, *promptstr, PromptMarksLength) + app.InsertCustomTableRecord(userid, *promptstr, PromptMarksLength) fmt.Printf("enhancedChoices=true,根据关键词切换prompt为: %s,newPromptStrStat:%d\n", *promptstr, PromptMarksLength) // 故事模式规则 应用 PromptChoiceQ 这一次是为了,替换了分支后,再次用新的分支的promptstr处理一次,因为原先的promptstr是跳转前,要用跳转后的再替换一次 app.ApplyPromptChoiceQ(*promptstr, requestmsg, message) @@ -323,7 +337,7 @@ func (app *App) ApplySwitchOnQ(promptstr *string, requestmsg *string, message *s *promptstr = selectedText // 获取新的信号长度 刷新持久化数据库 PromptMarksLength := config.GetPromptMarksLength(*promptstr) - app.InsertCustomTableRecord(message.UserID, *promptstr, PromptMarksLength) + app.InsertCustomTableRecord(userid, *promptstr, PromptMarksLength) fmt.Printf("enhancedChoices=false,根据关键词切换prompt为: %s,newPromptStrStat:%d\n", *promptstr, PromptMarksLength) // 故事模式规则 应用 PromptChoiceQ 这一次是为了,替换了分支后,再次用新的分支的promptstr处理一次,因为原先的promptstr是跳转前,要用跳转后的再替换一次 app.ApplyPromptChoiceQ(*promptstr, requestmsg, message) @@ -341,8 +355,13 @@ func (app *App) ProcessExitChoicesQ(promptstr string, requestmsg *string, messag return } + userid := message.UserID + if config.GetGroupContext() && message.MessageType != "private" { + userid = message.GroupID + } + // 获取用户剧情存档中的当前状态 - CustomRecord, err := app.FetchCustomRecord(message.UserID) + CustomRecord, err := app.FetchCustomRecord(userid) if err != nil { fmt.Printf("app.FetchCustomRecord 出错: %s\n", err) return @@ -413,8 +432,13 @@ func (app *App) ProcessExitChoicesQ(promptstr string, requestmsg *string, messag // HandleExit 处理用户退出逻辑,包括发送消息和重置用户状态。 func (app *App) HandleExit(exitText string, message *structs.OnebotGroupMessage, selfid string) { + userid := message.UserID + if config.GetGroupContext() && message.MessageType != "private" { + userid = message.GroupID + } + fmt.Printf("处理重置操作on:%v", exitText) - app.migrateUserToNewContext(message.UserID) + app.migrateUserToNewContext(userid) RestoreResponse := config.GetRandomRestoreResponses() if message.RealMessageType == "group_private" || message.MessageType == "private" { if !config.GetUsePrivateSSE() { @@ -425,7 +449,7 @@ func (app *App) HandleExit(exitText string, message *structs.OnebotGroupMessage, } else { utils.SendGroupMessage(message.GroupID, message.UserID, RestoreResponse, selfid) } - app.deleteCustomRecord(message.UserID) + app.deleteCustomRecord(userid) } // ProcessExitChoicesA 处理基于关键词的退出逻辑。 @@ -435,8 +459,13 @@ func (app *App) ProcessExitChoicesA(promptstr string, response *string, message return } + userid := message.UserID + if config.GetGroupContext() && message.MessageType != "private" { + userid = message.GroupID + } + // 获取用户剧情存档中的当前状态 - CustomRecord, err := app.FetchCustomRecord(message.UserID) + CustomRecord, err := app.FetchCustomRecord(userid) if err != nil { fmt.Printf("app.FetchCustomRecord 出错: %s\n", err) return @@ -507,11 +536,16 @@ func (app *App) ProcessExitChoicesA(promptstr string, response *string, message // ApplySwitchOnA 应用switchOnA的逻辑,动态修改promptstr func (app *App) ApplySwitchOnA(promptstr *string, response *string, message *structs.OnebotGroupMessage) { + userid := message.UserID + if config.GetGroupContext() && message.MessageType != "private" { + userid = message.GroupID + } + // 获取与 switchOnA 相关的选择 promptstrChoices := config.GetSwitchOnA(*promptstr) if len(promptstrChoices) != 0 { // 获取用户剧情存档中的当前状态 - CustomRecord, err := app.FetchCustomRecord(message.UserID) + CustomRecord, err := app.FetchCustomRecord(userid) if err != nil { fmt.Printf("app.FetchCustomRecord 出错: %s\n", err) return @@ -557,7 +591,7 @@ func (app *App) ApplySwitchOnA(promptstr *string, response *string, message *str if bestMatchCount > 0 { *promptstr = bestText PromptMarksLength := config.GetPromptMarksLength(*promptstr) - app.InsertCustomTableRecord(message.UserID, *promptstr, PromptMarksLength) + app.InsertCustomTableRecord(userid, *promptstr, PromptMarksLength) fmt.Printf("enhancedChoices=true,根据关键词A切换prompt为: %s,newPromptStrStat:%d\n", *promptstr, PromptMarksLength) } } @@ -577,7 +611,7 @@ func (app *App) ApplySwitchOnA(promptstr *string, response *string, message *str selectedText := texts[rand.Intn(len(texts))] // 随机选择一个文本 *promptstr = selectedText PromptMarksLength := config.GetPromptMarksLength(*promptstr) - app.InsertCustomTableRecord(message.UserID, *promptstr, PromptMarksLength) + app.InsertCustomTableRecord(userid, *promptstr, PromptMarksLength) fmt.Printf("enhancedChoices=false,根据关键词A切换prompt为: %s,newPromptStrStat:%d\n", *promptstr, PromptMarksLength) } } @@ -588,6 +622,11 @@ func (app *App) ApplySwitchOnA(promptstr *string, response *string, message *str // ApplyPromptChoiceA 应用故事模式的情绪增强逻辑,并返回增强内容。 func (app *App) ApplyPromptChoiceA(promptstr string, response string, message *structs.OnebotGroupMessage) string { + userid := message.UserID + if config.GetGroupContext() && message.MessageType != "private" { + userid = message.GroupID + } + promptChoices := config.GetPromptChoicesA(promptstr) if len(promptChoices) == 0 { // 获取系统历史,但不包括系统消息 @@ -608,7 +647,7 @@ func (app *App) ApplyPromptChoiceA(promptstr string, response string, message *s } // 获取用户剧情存档中的当前状态 - CustomRecord, err := app.FetchCustomRecord(message.UserID) + CustomRecord, err := app.FetchCustomRecord(userid) if err != nil { fmt.Printf("app.FetchCustomRecord 出错: %s\n", err) return "" diff --git a/applogic/gensokyo.go b/applogic/gensokyo.go index a92b76e..953faa6 100644 --- a/applogic/gensokyo.go +++ b/applogic/gensokyo.go @@ -142,11 +142,19 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { return } } - - // 从数据库读取用户的剧情存档 - CustomRecord, err := app.FetchCustomRecord(message.UserID) - if err != nil { - fmt.Printf("app.FetchCustomRecord 出错: %s\n", err) + var CustomRecord *structs.CustomRecord + if config.GetGroupContext() && message.MessageType != "private" { + // 从数据库读取用户的剧情存档 + CustomRecord, err = app.FetchCustomRecord(message.GroupID) + if err != nil { + fmt.Printf("app.FetchCustomRecord 出错: %s\n", err) + } + } else { + // 从数据库读取用户的剧情存档 + CustomRecord, err = app.FetchCustomRecord(message.UserID) + if err != nil { + fmt.Printf("app.FetchCustomRecord 出错: %s\n", err) + } } var promptstr string @@ -163,14 +171,27 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { promptstr = CustomRecord.PromptStr fmt.Printf("刷新prompt参数: %s,newPromptStrStat:%d\n", promptstr, CustomRecord.PromptStrStat-1) newPromptStrStat := CustomRecord.PromptStrStat - 1 - err = app.InsertCustomTableRecord(message.UserID, promptstr, newPromptStrStat) - if err != nil { - fmt.Printf("app.InsertCustomTableRecord 出错: %s\n", err) + // 根据条件区分群和私聊 + if config.GetGroupContext() && message.MessageType != "private" { + err = app.InsertCustomTableRecord(message.GroupID, promptstr, newPromptStrStat) + if err != nil { + fmt.Printf("app.InsertCustomTableRecord 出错: %s\n", err) + } + } else { + err = app.InsertCustomTableRecord(message.UserID, promptstr, newPromptStrStat) + if err != nil { + fmt.Printf("app.InsertCustomTableRecord 出错: %s\n", err) + } } + } // MARK: 提示词之间 整体切换Q - app.ProcessPromptMarks(message.UserID, message.Message.(string), &promptstr) + if config.GetGroupContext() && message.MessageType != "private" { + app.ProcessPromptMarks(message.GroupID, message.Message.(string), &promptstr) + } else { + app.ProcessPromptMarks(message.UserID, message.Message.(string), &promptstr) + } // 提示词之间流转 达到信号量 markType := config.GetPromptMarkType(promptstr) @@ -189,7 +210,12 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } // 刷新新的提示词给用户目前的状态 新的场景应该从1开始 - app.InsertCustomTableRecord(message.UserID, newPromptStr, 1) + if config.GetGroupContext() && message.MessageType != "private" { + app.InsertCustomTableRecord(message.GroupID, newPromptStr, 1) + } else { + app.InsertCustomTableRecord(message.UserID, newPromptStr, 1) + } + fmt.Printf("流转prompt参数: %s,newPromptStrStat:%d\n", newPromptStr, 1) promptstr = newPromptStr } @@ -203,7 +229,12 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } // MARK: 提示词之间 整体切换Q 当用户没有存档时 - app.ProcessPromptMarks(message.UserID, message.Message.(string), &promptstr) + if config.GetGroupContext() && message.MessageType != "private" { + app.ProcessPromptMarks(message.GroupID, message.Message.(string), &promptstr) + } else { + app.ProcessPromptMarks(message.UserID, message.Message.(string), &promptstr) + } + var newstat int if config.GetPromptMarksLength(promptstr) > 1000 { newstat = config.GetPromptMarksLength(promptstr) @@ -212,7 +243,12 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } // 初始状态就是 1 设置了1000以上长度的是固有场景,不可切换 - err = app.InsertCustomTableRecord(message.UserID, promptstr, newstat) + if config.GetGroupContext() && message.MessageType != "private" { + err = app.InsertCustomTableRecord(message.GroupID, promptstr, newstat) + } else { + err = app.InsertCustomTableRecord(message.UserID, promptstr, newstat) + } + if err != nil { fmt.Printf("app.InsertCustomTableRecord 出错: %s\n", err) } @@ -297,7 +333,11 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { //处理重置指令 if isResetCommand { fmtf.Println("处理重置操作") - app.migrateUserToNewContext(message.UserID) + if config.GetGroupContext() && message.MessageType != "private" { + app.migrateUserToNewContext(message.GroupID) + } else { + app.migrateUserToNewContext(message.UserID) + } RestoreResponse := config.GetRandomRestoreResponses() if message.RealMessageType == "group_private" || message.MessageType == "private" { if !config.GetUsePrivateSSE() { @@ -309,7 +349,11 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { utils.SendGroupMessage(message.GroupID, message.UserID, RestoreResponse, selfid) } // 处理故事情节的重置 - app.deleteCustomRecord(message.UserID) + if config.GetGroupContext() && message.MessageType != "private" { + app.deleteCustomRecord(message.GroupID) + } else { + app.deleteCustomRecord(message.UserID) + } return } @@ -517,7 +561,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // 处理保存记忆 if ismemoryCommand { - app.handleSaveMemory(message, conversationID, parentMessageID) + app.handleSaveMemory(message, conversationID, parentMessageID) // 适配群 return } @@ -539,13 +583,13 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // 处理记忆列表 if ismemoryLoadCommand { - app.handleMemoryList(message) + app.handleMemoryList(message) // 适配群 return } // 新增处理载入记忆的逻辑 if isPrefixedMemoryLoadCommand { - app.handleLoadMemory(message, checkResetCommand) + app.handleLoadMemory(message, checkResetCommand) // 适配群 return } @@ -563,7 +607,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // 处理新对话 if isnewConversationCommand { - app.handleNewConversation(message, conversationID, parentMessageID) + app.handleNewConversation(message, conversationID, parentMessageID) // 适配群 return } @@ -597,21 +641,29 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } // 关键词退出部分ExitChoicesQ - app.ProcessExitChoicesQ(promptstr, &requestmsg, &message, selfid) + app.ProcessExitChoicesQ(promptstr, &requestmsg, &message, selfid) // 适配群 // 故事模式规则 应用 PromptChoiceQ - app.ApplyPromptChoiceQ(promptstr, &requestmsg, &message) + app.ApplyPromptChoiceQ(promptstr, &requestmsg, &message) // 适配群 // 故事模式规则 应用 PromptCoverQ - app.ApplyPromptCoverQ(promptstr, &requestmsg, &message) + app.ApplyPromptCoverQ(promptstr, &requestmsg, &message) // 适配群 // promptstr 随 switchOnQ 变化 切换Q - app.ApplySwitchOnQ(&promptstr, &requestmsg, &message) + app.ApplySwitchOnQ(&promptstr, &requestmsg, &message) // 适配群 // 从数据库读取用户的剧情存档 - CustomRecord, err := app.FetchCustomRecord(message.UserID) - if err != nil { - fmt.Printf("app.FetchCustomRecord 出错: %s\n", err) + var CustomRecord *structs.CustomRecord + if config.GetGroupContext() && message.MessageType != "private" { + CustomRecord, err = app.FetchCustomRecord(message.GroupID) + if err != nil { + fmt.Printf("app.FetchCustomRecord 出错: %s\n", err) + } + } else { + CustomRecord, err = app.FetchCustomRecord(message.UserID) + if err != nil { + fmt.Printf("app.FetchCustomRecord 出错: %s\n", err) + } } // 生成场景 @@ -620,8 +672,11 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { PromptMarksLength := config.GetPromptMarksLength(promptstr) app.GetAndSendEnv(requestmsg, promptstr, message, selfid, CustomRecord.PromptStrStat, PromptMarksLength) } - - fmtf.Printf("实际请求conversation端点内容:[%v]%v\n", message.UserID, requestmsg) + if config.GetGroupContext() && message.MessageType != "private" { + fmtf.Printf("实际请求conversation端点内容:[%v]%v\n", message.GroupID, requestmsg) + } else { + fmtf.Printf("实际请求conversation端点内容:[%v]%v\n", message.UserID, requestmsg) + } requestBody, err := json.Marshal(map[string]interface{}{ "message": requestmsg, diff --git a/applogic/memory.go b/applogic/memory.go index 3979203..ce6ece5 100644 --- a/applogic/memory.go +++ b/applogic/memory.go @@ -84,8 +84,13 @@ func GetMemoryTitle(msg string) string { func (app *App) handleSaveMemory(msg structs.OnebotGroupMessage, ConversationID string, ParentMessageID string) { conversationTitle := "2024-5-19/18:26" // 默认标题,根据实际需求可能需要调整为动态生成的时间戳 + userid := msg.UserID + if config.GetGroupContext() && msg.MessageType != "private" { + userid = msg.GroupID + } + // 添加用户记忆 - err := app.AddUserMemory(msg.UserID, ConversationID, ParentMessageID, conversationTitle) + err := app.AddUserMemory(userid, ConversationID, ParentMessageID, conversationTitle) if err != nil { log.Printf("Error saving memory: %s", err) return @@ -104,7 +109,7 @@ func (app *App) handleSaveMemory(msg structs.OnebotGroupMessage, ConversationID newTitle := GetMemoryTitle(memoryTitle) // 获取最终的记忆标题 // 更新记忆标题 - err = app.updateConversationTitle(msg.UserID, ConversationID, ParentMessageID, newTitle) + err = app.updateConversationTitle(userid, ConversationID, ParentMessageID, newTitle) if err != nil { log.Printf("Error updating conversation title: %s", err) } @@ -124,7 +129,13 @@ func (app *App) handleSaveMemory(msg structs.OnebotGroupMessage, ConversationID // 获取记忆列表 func (app *App) handleMemoryList(msg structs.OnebotGroupMessage) { - memories, err := app.GetUserMemories(msg.UserID) + + userid := msg.UserID + if config.GetGroupContext() && msg.MessageType != "private" { + userid = msg.GroupID + } + + memories, err := app.GetUserMemories(userid) if err != nil { log.Printf("Error retrieving memories: %s", err) return @@ -166,6 +177,12 @@ func (app *App) handleMemoryList(msg structs.OnebotGroupMessage) { // 载入记忆 func (app *App) handleLoadMemory(msg structs.OnebotGroupMessage, checkResetCommand string) { + + userid := msg.UserID + if config.GetGroupContext() && msg.MessageType != "private" { + userid = msg.GroupID + } + // 从配置获取载入记忆指令 memoryLoadCommands := config.GetMemoryLoadCommand() @@ -178,7 +195,7 @@ func (app *App) handleLoadMemory(msg structs.OnebotGroupMessage, checkResetComma matchTerm := strings.TrimSpace(checkResetCommand) // 获取用户记忆 - memories, err := app.GetUserMemories(msg.UserID) + memories, err := app.GetUserMemories(userid) if err != nil { log.Printf("Error retrieving memories: %s", err) app.sendMemoryResponse(msg, "获取记忆失败") @@ -200,7 +217,7 @@ func (app *App) handleLoadMemory(msg structs.OnebotGroupMessage, checkResetComma } // 载入记忆 - err = app.updateUserContextPro(msg.UserID, matchedMemory.ConversationID, matchedMemory.ParentMessageID) + err = app.updateUserContextPro(userid, matchedMemory.ConversationID, matchedMemory.ParentMessageID) if err != nil { log.Printf("Error adding memory: %s", err) app.sendMemoryResponse(msg, "载入记忆失败") @@ -266,9 +283,14 @@ func formatHistory(history []structs.Message) string { func (app *App) handleNewConversation(msg structs.OnebotGroupMessage, conversationID string, parentMessageID string) { // 使用预定义的时间戳作为会话标题 conversationTitle := "2024-5-19/18:26" // 实际应用中应使用动态生成的时间戳 + userid := msg.UserID + + if config.GetGroupContext() && msg.MessageType != "private" { + userid = msg.GroupID + } // 添加用户记忆 - err := app.AddUserMemory(msg.UserID, conversationID, parentMessageID, conversationTitle) + err := app.AddUserMemory(userid, conversationID, parentMessageID, conversationTitle) if err != nil { log.Printf("Error saving memory: %s", err) return @@ -287,14 +309,14 @@ func (app *App) handleNewConversation(msg structs.OnebotGroupMessage, conversati newTitle := GetMemoryTitle(memoryTitle) // 获取最终的记忆标题 // 更新记忆标题 - err = app.updateConversationTitle(msg.UserID, conversationID, parentMessageID, newTitle) + err = app.updateConversationTitle(userid, conversationID, parentMessageID, newTitle) if err != nil { log.Printf("Error updating conversation title: %s", err) } }() // 迁移用户到新的上下文 - app.migrateUserToNewContext(msg.UserID) + app.migrateUserToNewContext(userid) // 获取并使用配置中指定的加载记忆指令 loadCommand := config.GetMemoryLoadCommand() From 151d6f7f46bbe001b50be9b63ee1d94ea0f28916 Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Thu, 6 Jun 2024 09:23:23 +0800 Subject: [PATCH 24/30] Beta137 (#136) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 * beta117 * beta118 * beta119 * beta120 * beta121 * beta122 * beta123 * beta124 * beta125 * beta126 * beta127 * beta128 * beta129 * beta130 * beta131 * beta132 * beta1333 * beta134 * beta135 * beta136 * beta137 --- applogic/gensokyo.go | 38 +++++++++--- applogic/yuanqi.go | 1 + config/config.go | 112 +++++++++++++++++++++++++++++++++--- prompt/prompt.go | 14 +++++ server/server.go | 2 +- structs/struct.go | 1 + template/config_template.go | 1 + utils/utils.go | 6 +- 8 files changed, 153 insertions(+), 22 deletions(-) diff --git a/applogic/gensokyo.go b/applogic/gensokyo.go index 953faa6..6e88ed7 100644 --- a/applogic/gensokyo.go +++ b/applogic/gensokyo.go @@ -16,6 +16,7 @@ import ( "github.com/hoshinonyaruko/gensokyo-llm/acnode" "github.com/hoshinonyaruko/gensokyo-llm/config" "github.com/hoshinonyaruko/gensokyo-llm/fmtf" + "github.com/hoshinonyaruko/gensokyo-llm/prompt" "github.com/hoshinonyaruko/gensokyo-llm/structs" "github.com/hoshinonyaruko/gensokyo-llm/utils" ) @@ -134,14 +135,27 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { return } - // 判断是否是群聊 然后判断触发词 + // 判断是否是群聊,然后检查触发词 if message.RealMessageType != "group_private" && message.MessageType != "private" { if !checkMessageForHints(utils.RemoveBracketsContent(message.RawMessage)) { - w.WriteHeader(http.StatusOK) - w.Write([]byte("Group message not hint words.")) - return + // 获取概率值 + chance := config.GetGroupHintChance() + + // 生成0-100之间的随机数 + randomValue := rand.Intn(100) + + // 比较随机值与配置中的概率 + if randomValue >= chance { + w.WriteHeader(http.StatusOK) + w.Write([]byte("Group message not hint words.")) + return + } else { + // 记录日志,表明概率检查通过 + fmt.Printf("Probability check passed: %d%% chance, random value: %d\n", chance, randomValue) + } } } + var CustomRecord *structs.CustomRecord if config.GetGroupContext() && message.MessageType != "private" { // 从数据库读取用户的剧情存档 @@ -709,6 +723,12 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { baseURL = config.GetLotus(promptstr) + basePath } + // 在加入prompt之前 判断promptstr.yml是否存在 + if !prompt.CheckPromptExistence(promptstr) { + fmtf.Printf("该请求内容所对应yml文件不存在:[%v]:[%v]\n", requestmsg, promptstr) + promptstr = "" + } + // 使用net/url包来构建和编码URL urlParams := url.Values{} if promptstr != "" { @@ -903,7 +923,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { if !config.GetHideExtraLogs() { fmtf.Printf("收到流数据,切割并发送信息: %s", string(line)) } - splitAndSendMessages(string(line), newmsg, selfid) + splitAndSendMessages(string(line), newmsg, selfid, promptstr) } } } @@ -1085,7 +1105,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } -func splitAndSendMessages(line string, newmesssage string, selfid string) { +func splitAndSendMessages(line string, newmesssage string, selfid string, promptstr string) { // 提取JSON部分 dataPrefix := "data: " jsonStr := strings.TrimPrefix(line, dataPrefix) @@ -1103,13 +1123,13 @@ func splitAndSendMessages(line string, newmesssage string, selfid string) { if sseData.Response != "\n\n" { // 处理提取出的信息 - processMessage(sseData.Response, sseData.ConversationId, newmesssage, selfid) + processMessage(sseData.Response, sseData.ConversationId, newmesssage, selfid, promptstr) } else { fmtf.Printf("忽略llm末尾的换行符") } } -func processMessage(response string, conversationid string, newmesssage string, selfid string) { +func processMessage(response string, conversationid string, newmesssage string, selfid string, promptstr string) { // 从conversation对应的sync map取出对应的用户和群号,避免高并发内容发送错乱 userinfo, _ := GetUserInfo(conversationid) key := utils.GetKey(userinfo.GroupID, userinfo.UserID) @@ -1119,7 +1139,7 @@ func processMessage(response string, conversationid string, newmesssage string, for _, char := range response { AppendRune(conversationid, char) - if utils.ContainsRune(punctuations, char, userinfo.GroupID) { + if utils.ContainsRune(punctuations, char, userinfo.GroupID, promptstr) { // 达到标点符号,发送累积的整个消息 if GetMessageLength(conversationid) > 0 { accumulatedMessage, _ := GetCurrentMessage(conversationid) diff --git a/applogic/yuanqi.go b/applogic/yuanqi.go index f5dc2bf..b88a061 100644 --- a/applogic/yuanqi.go +++ b/applogic/yuanqi.go @@ -153,6 +153,7 @@ func (app *App) ChatHandlerYuanQi(w http.ResponseWriter, r *http.Request) { systemHistory, err := prompt.GetMessagesExcludingSystem(promptstr) if err != nil { fmtf.Printf("Error getting system history: %v\n", err) + // 如果进行到不存在的分支,这里就return了,导致没有回复 但如果不return,模型是没有system提示词的原始状态,所以要在前面做处理 return } diff --git a/config/config.go b/config/config.go index 1b9fff1..f91c204 100644 --- a/config/config.go +++ b/config/config.go @@ -536,23 +536,117 @@ func GetGroupmessage() bool { } // 获取SplitByPuntuations -func GetSplitByPuntuations() int { +func GetSplitByPuntuations(options ...string) int { mu.Lock() defer mu.Unlock() - if instance != nil { - return instance.Settings.SplitByPuntuations + return getSplitByPuntuationsInternal(options...) +} + +// 内部逻辑执行函数,不处理锁,可以安全地递归调用 +func getSplitByPuntuationsInternal(options ...string) int { + // 检查是否有参数传递进来,以及是否为空字符串 + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.SplitByPuntuations + } + return 0 // 默认值或错误处理 } - return 0 + + // 使用传入的 basename 来查找特定配置 + basename := options[0] + SplitByPuntuationsInterface, err := prompt.GetSettingFromFilename(basename, "SplitByPuntuations") + if err != nil { + log.Println("Error retrieving SplitByPuntuations:", err) + return getSplitByPuntuationsInternal() // 递归调用内部函数,不传递任何参数 + } + + SplitByPuntuations, ok := SplitByPuntuationsInterface.(int) + if !ok { // 检查类型断言是否失败 + fmt.Println("Type assertion failed for SplitByPuntuations, fetching default") + return getSplitByPuntuationsInternal() // 递归调用内部函数,不传递任何参数 + } + + if SplitByPuntuations == 0 { + return getSplitByPuntuationsInternal() // 递归调用内部函数,不传递任何参数 + } + + return SplitByPuntuations } -// 获取SplitByPuntuationsGroup -func GetSplitByPuntuationsGroup() int { +// 获取GetSplitByPuntuationsGroup +func GetSplitByPuntuationsGroup(options ...string) int { mu.Lock() defer mu.Unlock() - if instance != nil { - return instance.Settings.SplitByPuntuationsGroup + return getSplitByPuntuationsGroupInternal(options...) +} + +// 内部逻辑执行函数,不处理锁,可以安全地递归调用 +func getSplitByPuntuationsGroupInternal(options ...string) int { + // 检查是否有参数传递进来,以及是否为空字符串 + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.SplitByPuntuationsGroup + } + return 0 // 默认值或错误处理 } - return 0 + + // 使用传入的 basename 来查找特定配置 + basename := options[0] + SplitByPuntuationsGroupInterface, err := prompt.GetSettingFromFilename(basename, "SplitByPuntuationsGroup") + if err != nil { + log.Println("Error retrieving SplitByPuntuationsGroup:", err) + return getSplitByPuntuationsGroupInternal() // 递归调用内部函数,不传递任何参数 + } + + SplitByPuntuationsGroup, ok := SplitByPuntuationsGroupInterface.(int) + if !ok { // 检查类型断言是否失败 + fmt.Println("Type assertion failed for SplitByPuntuationsGroup, fetching default") + return getSplitByPuntuationsGroupInternal() // 递归调用内部函数,不传递任何参数 + } + + if SplitByPuntuationsGroup == 0 { + return getSplitByPuntuationsGroupInternal() // 递归调用内部函数,不传递任何参数 + } + + return SplitByPuntuationsGroup +} + +// 获取GroupHintChance +func GetGroupHintChance(options ...string) int { + mu.Lock() + defer mu.Unlock() + return getGroupHintChanceInternal(options...) +} + +// 内部逻辑执行函数,不处理锁,可以安全地递归调用 +func getGroupHintChanceInternal(options ...string) int { + // 检查是否有参数传递进来,以及是否为空字符串 + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.GroupHintChance + } + return 0 // 默认值或错误处理 + } + + // 使用传入的 basename 来查找特定配置 + basename := options[0] + GroupHintChanceInterface, err := prompt.GetSettingFromFilename(basename, "GroupHintChance") + if err != nil { + log.Println("Error retrieving GroupHintChance:", err) + return getGroupHintChanceInternal() // 递归调用内部函数,不传递任何参数 + } + + GroupHintChance, ok := GroupHintChanceInterface.(int) + if !ok { // 检查类型断言是否失败 + fmt.Println("Type assertion failed for GroupHintChance, fetching default") + return getGroupHintChanceInternal() // 递归调用内部函数,不传递任何参数 + } + + if GroupHintChance == 0 { + return getGroupHintChanceInternal() // 递归调用内部函数,不传递任何参数 + } + + return GroupHintChance } // 获取HunyuanType diff --git a/prompt/prompt.go b/prompt/prompt.go index b4b2ca4..a8d3ad8 100644 --- a/prompt/prompt.go +++ b/prompt/prompt.go @@ -276,3 +276,17 @@ func GetSettingFromFilename(basename, settingName string) (interface{}, error) { // 返回字段的值,转换为interface{} return field.Interface(), nil } + +// CheckPromptExistence 检查基于 basename 组合的 filename 是否存在于 promptsCache 中 +func CheckPromptExistence(basename string) bool { + // 组合 basename 和 ".yml" 形成完整的文件名 + filename := basename + ".yml" + + // 用读锁来确保并发安全 + lock.RLock() + defer lock.RUnlock() + + // 检查文件是否存在于缓存中 + _, exists := promptsCache[filename] + return exists +} diff --git a/server/server.go b/server/server.go index bfcc210..19c5da1 100644 --- a/server/server.go +++ b/server/server.go @@ -100,7 +100,7 @@ func WsHandler(w http.ResponseWriter, r *http.Request, config *config.Config) { conn, err := upgrader.Upgrade(w, r, nil) if err != nil { - log.Printf("Failed to set websocket upgrade: %+v", err) + log.Printf("Failed to set websocket upgrade[%v]: %+v", r.Header, err) return } defer conn.Close() diff --git a/structs/struct.go b/structs/struct.go index 9fe5828..41cfa19 100644 --- a/structs/struct.go +++ b/structs/struct.go @@ -264,6 +264,7 @@ type Settings struct { UrlSendPics bool `yaml:"urlSendPics"` // 自己构造图床加速图片发送 MdPromptKeyboardAtGroup bool `yaml:"mdPromptKeyboardAtGroup"` // 群内使用md能力模拟PromptKeyboard GroupHintWords []string `yaml:"groupHintWords"` + GroupHintChance int `yaml:"groupHintChance"` GroupContext bool `yaml:"groupContext"` HunyuanType int `yaml:"hunyuanType"` diff --git a/template/config_template.go b/template/config_template.go index a8739a3..08ade01 100644 --- a/template/config_template.go +++ b/template/config_template.go @@ -49,6 +49,7 @@ settings: hideExtraLogs : false #忽略流信息的log,提高性能 urlSendPics : false #自己构造图床加速图片发送.需配置公网ip+放通port+设置正确的selfPath groupHintWords : [] #当机器人位于群内时,需满足包含groupHintWords数组任意内容如[CQ:at,qq=2] 机器人的名字 等 + groupHintChance : 0 #需与groupHintWords联用,代表不满足hintwords时概率触发,不启用groupHintWords相当于百分百概率回复. groupContext : false #群上下文 在智能体在群内时,以群为单位处理上下文. #Ws服务器配置 diff --git a/utils/utils.go b/utils/utils.go index 85a59b9..8022efc 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -148,14 +148,14 @@ func GetKey(groupid int64, userid int64) string { } // 随机的分布发送 -func ContainsRune(slice []rune, value rune, groupid int64) bool { +func ContainsRune(slice []rune, value rune, groupid int64, promptstr string) bool { var probability int if groupid == 0 { // 获取概率百分比 - probability = config.GetSplitByPuntuations() + probability = config.GetSplitByPuntuations(promptstr) } else { // 获取概率百分比 - probability = config.GetSplitByPuntuationsGroup() + probability = config.GetSplitByPuntuationsGroup(promptstr) } for _, item := range slice { From 4e2534a8c484236ff6d56f18186e91704cb26cc5 Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Thu, 6 Jun 2024 14:43:54 +0800 Subject: [PATCH 25/30] Beta138 (#137) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 * beta117 * beta118 * beta119 * beta120 * beta121 * beta122 * beta123 * beta124 * beta125 * beta126 * beta127 * beta128 * beta129 * beta130 * beta131 * beta132 * beta1333 * beta134 * beta135 * beta136 * beta137 * beta138 --- applogic/env.go | 2 +- applogic/flow.go | 16 ++-- applogic/gensokyo.go | 72 +++++++++-------- applogic/memory.go | 46 +++++------ applogic/vectorsensitive.go | 8 +- config/config.go | 151 +++++++++++++++++++----------------- structs/struct.go | 48 +++++++----- template/config_template.go | 6 ++ utils/blacklist.go | 14 ++-- utils/utils.go | 104 +++++++++++++++++++------ 10 files changed, 272 insertions(+), 195 deletions(-) diff --git a/applogic/env.go b/applogic/env.go index 065e124..2bf57a6 100644 --- a/applogic/env.go +++ b/applogic/env.go @@ -105,7 +105,7 @@ func (app *App) GetAndSendEnv(msg string, promptstr string, message structs.Oneb if message.RealMessageType == "group_private" || message.MessageType == "private" { utils.SendPrivateMessageRaw(message.UserID, newResponse, selfid) } else { - utils.SendGroupMessage(message.GroupID, message.UserID, newResponse, selfid) + utils.SendGroupMessage(message.GroupID, message.UserID, newResponse, selfid, promptstr) } } diff --git a/applogic/flow.go b/applogic/flow.go index 605ff7b..99a5d05 100644 --- a/applogic/flow.go +++ b/applogic/flow.go @@ -404,7 +404,7 @@ func (app *App) ProcessExitChoicesQ(promptstr string, requestmsg *string, messag } } if bestMatchCount > 0 { - app.HandleExit(bestText, message, selfid) + app.HandleExit(bestText, message, selfid, promptstr) return } } @@ -422,7 +422,7 @@ func (app *App) ProcessExitChoicesQ(promptstr string, requestmsg *string, messag texts := strings.Split(addedTexts, "-") if len(texts) > 0 { selectedText := texts[rand.Intn(len(texts))] - app.HandleExit(selectedText, message, selfid) + app.HandleExit(selectedText, message, selfid, promptstr) return } } @@ -431,7 +431,7 @@ func (app *App) ProcessExitChoicesQ(promptstr string, requestmsg *string, messag } // HandleExit 处理用户退出逻辑,包括发送消息和重置用户状态。 -func (app *App) HandleExit(exitText string, message *structs.OnebotGroupMessage, selfid string) { +func (app *App) HandleExit(exitText string, message *structs.OnebotGroupMessage, selfid string, promptstr string) { userid := message.UserID if config.GetGroupContext() && message.MessageType != "private" { userid = message.GroupID @@ -442,12 +442,12 @@ func (app *App) HandleExit(exitText string, message *structs.OnebotGroupMessage, RestoreResponse := config.GetRandomRestoreResponses() if message.RealMessageType == "group_private" || message.MessageType == "private" { if !config.GetUsePrivateSSE() { - utils.SendPrivateMessage(message.UserID, RestoreResponse, selfid) + utils.SendPrivateMessage(message.UserID, RestoreResponse, selfid, promptstr) } else { - utils.SendSSEPrivateRestoreMessage(message.UserID, RestoreResponse) + utils.SendSSEPrivateRestoreMessage(message.UserID, RestoreResponse, promptstr) } } else { - utils.SendGroupMessage(message.GroupID, message.UserID, RestoreResponse, selfid) + utils.SendGroupMessage(message.GroupID, message.UserID, RestoreResponse, selfid, promptstr) } app.deleteCustomRecord(userid) } @@ -508,7 +508,7 @@ func (app *App) ProcessExitChoicesA(promptstr string, response *string, message } } if bestMatchCount > 0 { - app.HandleExit(bestText, message, selfid) + app.HandleExit(bestText, message, selfid, promptstr) return } } @@ -526,7 +526,7 @@ func (app *App) ProcessExitChoicesA(promptstr string, response *string, message texts := strings.Split(addedTexts, "-") if len(texts) > 0 { selectedText := texts[rand.Intn(len(texts))] - app.HandleExit(selectedText, message, selfid) + app.HandleExit(selectedText, message, selfid, promptstr) return } } diff --git a/applogic/gensokyo.go b/applogic/gensokyo.go index 6e88ed7..cd5bd86 100644 --- a/applogic/gensokyo.go +++ b/applogic/gensokyo.go @@ -339,7 +339,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } } - if utils.BlacklistIntercept(message, selfid) { + if utils.BlacklistIntercept(message, selfid, promptstr) { fmtf.Printf("userid:[%v]groupid:[%v]这位用户或群在黑名单中,被拦截", message.UserID, message.GroupID) return } @@ -355,12 +355,12 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { RestoreResponse := config.GetRandomRestoreResponses() if message.RealMessageType == "group_private" || message.MessageType == "private" { if !config.GetUsePrivateSSE() { - utils.SendPrivateMessage(message.UserID, RestoreResponse, selfid) + utils.SendPrivateMessage(message.UserID, RestoreResponse, selfid, promptstr) } else { - utils.SendSSEPrivateRestoreMessage(message.UserID, RestoreResponse) + utils.SendSSEPrivateRestoreMessage(message.UserID, RestoreResponse, promptstr) } } else { - utils.SendGroupMessage(message.GroupID, message.UserID, RestoreResponse, selfid) + utils.SendGroupMessage(message.GroupID, message.UserID, RestoreResponse, selfid, promptstr) } // 处理故事情节的重置 if config.GetGroupContext() && message.MessageType != "private" { @@ -402,7 +402,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // 进行字数拦截 if config.GetQuestionMaxLenth() != 0 { - if utils.LengthIntercept(newmsg, message, selfid) { + if utils.LengthIntercept(newmsg, message, selfid, promptstr) { fmtf.Printf("字数过长,可在questionMaxLenth配置项修改,Q: %v", newmsg) // 发送响应 w.WriteHeader(http.StatusOK) @@ -413,7 +413,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // 进行语言判断拦截 skipLangCheck为false时 if len(config.GetAllowedLanguages()) > 0 && !skipLangCheck { - if utils.LanguageIntercept(newmsg, message, selfid) { + if utils.LanguageIntercept(newmsg, message, selfid, promptstr) { fmtf.Printf("不安全!不支持的语言,可在config.yml设置允许的语言,allowedLanguages配置项,Q: %v", newmsg) // 发送响应 w.WriteHeader(http.StatusOK) @@ -440,7 +440,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // 向量安全词部分,机器人向量安全屏障 if config.GetVectorSensitiveFilter() { - ret, retstr, err := app.InterceptSensitiveContent(vector, message, selfid) + ret, retstr, err := app.InterceptSensitiveContent(vector, message, selfid, promptstr) if err != nil { fmtf.Printf("Error in InterceptSensitiveContent: %v", err) // 发送响应 @@ -490,12 +490,12 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // 发送响应消息 if message.RealMessageType == "group_private" || message.MessageType == "private" { if !config.GetUsePrivateSSE() { - utils.SendPrivateMessage(message.UserID, responseText, selfid) + utils.SendPrivateMessage(message.UserID, responseText, selfid, promptstr) } else { - utils.SendSSEPrivateMessage(message.UserID, responseText) + utils.SendSSEPrivateMessage(message.UserID, responseText, promptstr) } } else { - utils.SendGroupMessage(message.GroupID, message.UserID, responseText, selfid) + utils.SendGroupMessage(message.GroupID, message.UserID, responseText, selfid, promptstr) } // 发送响应 w.WriteHeader(http.StatusOK) @@ -535,12 +535,12 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { if saveresponse != "" { if message.RealMessageType == "group_private" || message.MessageType == "private" { if !config.GetUsePrivateSSE() { - utils.SendPrivateMessage(message.UserID, saveresponse, selfid) + utils.SendPrivateMessage(message.UserID, saveresponse, selfid, promptstr) } else { - utils.SendSSEPrivateSafeMessage(message.UserID, saveresponse) + utils.SendSSEPrivateSafeMessage(message.UserID, saveresponse, promptstr) } } else { - utils.SendGroupMessage(message.GroupID, message.UserID, saveresponse, selfid) + utils.SendGroupMessage(message.GroupID, message.UserID, saveresponse, selfid, promptstr) } } // 发送响应 @@ -575,7 +575,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // 处理保存记忆 if ismemoryCommand { - app.handleSaveMemory(message, conversationID, parentMessageID) // 适配群 + app.handleSaveMemory(message, conversationID, parentMessageID, promptstr) // 适配群 return } @@ -597,13 +597,13 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // 处理记忆列表 if ismemoryLoadCommand { - app.handleMemoryList(message) // 适配群 + app.handleMemoryList(message, promptstr) // 适配群 return } // 新增处理载入记忆的逻辑 if isPrefixedMemoryLoadCommand { - app.handleLoadMemory(message, checkResetCommand) // 适配群 + app.handleLoadMemory(message, checkResetCommand, promptstr) // 适配群 return } @@ -621,7 +621,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // 处理新对话 if isnewConversationCommand { - app.handleNewConversation(message, conversationID, parentMessageID) // 适配群 + app.handleNewConversation(message, conversationID, parentMessageID, promptstr) // 适配群 return } @@ -686,6 +686,10 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { PromptMarksLength := config.GetPromptMarksLength(promptstr) app.GetAndSendEnv(requestmsg, promptstr, message, selfid, CustomRecord.PromptStrStat, PromptMarksLength) } + + // 按提示词区分的细化替换 这里主要不是为了安全和敏感词,而是细化效果,也就没有使用acnode提高效率 + requestmsg = utils.ReplaceTextIn(requestmsg, promptstr) + if config.GetGroupContext() && message.MessageType != "private" { fmtf.Printf("实际请求conversation端点内容:[%v]%v\n", message.GroupID, requestmsg) } else { @@ -817,7 +821,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // 判断消息类型,如果是私人消息或私有群消息,发送私人消息;否则,根据配置决定是否发送群消息 if userinfo.RealMessageType == "group_private" || userinfo.MessageType == "private" { if !config.GetUsePrivateSSE() { - utils.SendPrivateMessage(userinfo.UserID, newPart, selfid) + utils.SendPrivateMessage(userinfo.UserID, newPart, selfid, promptstr) } else { //判断是否最后一条 var state int @@ -830,16 +834,16 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { Content: newPart, State: state, } - utils.SendPrivateMessageSSE(userinfo.UserID, messageSSE) + utils.SendPrivateMessageSSE(userinfo.UserID, messageSSE, promptstr) } } else { // 这里发送的是newPart api最后补充的部分 if !config.GetMdPromptKeyboardAtGroup() { // 如果没有 EnhancedAContent if EnhancedAContent == "" { - utils.SendGroupMessage(userinfo.GroupID, userinfo.UserID, newPart, selfid) + utils.SendGroupMessage(userinfo.GroupID, userinfo.UserID, newPart, selfid, promptstr) } else { - utils.SendGroupMessage(userinfo.GroupID, userinfo.UserID, newPart+EnhancedAContent, selfid) + utils.SendGroupMessage(userinfo.GroupID, userinfo.UserID, newPart+EnhancedAContent, selfid, promptstr) } } else { // 如果没有 EnhancedAContent @@ -864,9 +868,9 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { if !config.GetUsePrivateSSE() { // 如果没有 EnhancedAContent if EnhancedAContent == "" { - utils.SendPrivateMessage(userinfo.UserID, response, selfid) + utils.SendPrivateMessage(userinfo.UserID, response, selfid, promptstr) } else { - utils.SendPrivateMessage(userinfo.UserID, response+EnhancedAContent, selfid) + utils.SendPrivateMessage(userinfo.UserID, response+EnhancedAContent, selfid, promptstr) } } else { //判断是否最后一条 @@ -880,15 +884,15 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { Content: response, State: state, } - utils.SendPrivateMessageSSE(userinfo.UserID, messageSSE) + utils.SendPrivateMessageSSE(userinfo.UserID, messageSSE, promptstr) } } else { if !config.GetMdPromptKeyboardAtGroup() { // 如果没有 EnhancedAContent if EnhancedAContent == "" { - utils.SendGroupMessage(userinfo.GroupID, userinfo.UserID, response, selfid) + utils.SendGroupMessage(userinfo.GroupID, userinfo.UserID, response, selfid, promptstr) } else { - utils.SendGroupMessage(userinfo.GroupID, userinfo.UserID, response+EnhancedAContent, selfid) + utils.SendGroupMessage(userinfo.GroupID, userinfo.UserID, response+EnhancedAContent, selfid, promptstr) } } else { // 如果没有 EnhancedAContent @@ -936,7 +940,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { Content: EnhancedAContent, State: 11, } - utils.SendPrivateMessageSSE(message.UserID, messageSSE) + utils.SendPrivateMessageSSE(message.UserID, messageSSE, promptstr) } } } @@ -1015,7 +1019,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { State: 20, PromptKeyboard: promptkeyboard, } - utils.SendPrivateMessageSSE(message.UserID, messageSSE) + utils.SendPrivateMessageSSE(message.UserID, messageSSE, promptstr) ResetIndex(newmsg) } } @@ -1041,9 +1045,9 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { if response, ok = responseData["response"].(string); ok && response != "" { // 判断消息类型,如果是私人消息或私有群消息,发送私人消息;否则,根据配置决定是否发送群消息 if message.RealMessageType == "group_private" || message.MessageType == "private" { - utils.SendPrivateMessage(message.UserID, response, selfid) + utils.SendPrivateMessage(message.UserID, response, selfid, promptstr) } else { - utils.SendGroupMessage(message.GroupID, message.UserID, response, selfid) + utils.SendGroupMessage(message.GroupID, message.UserID, response, selfid, promptstr) } } @@ -1157,7 +1161,7 @@ func processMessage(response string, conversationid string, newmesssage string, // 判断消息类型,如果是私人消息或私有群消息,发送私人消息;否则,根据配置决定是否发送群消息 if userinfo.RealMessageType == "group_private" || userinfo.MessageType == "private" { if !config.GetUsePrivateSSE() { - utils.SendPrivateMessage(userinfo.UserID, accumulatedMessage, selfid) + utils.SendPrivateMessage(userinfo.UserID, accumulatedMessage, selfid, promptstr) } else { if IncrementIndex(newmesssage) == 1 { //第一条信息 @@ -1170,18 +1174,18 @@ func processMessage(response string, conversationid string, newmesssage string, ActionButton: 10, CallbackData: uerid, } - utils.SendPrivateMessageSSE(userinfo.UserID, messageSSE) + utils.SendPrivateMessageSSE(userinfo.UserID, messageSSE, promptstr) } else { //SSE的前半部分 messageSSE := structs.InterfaceBody{ Content: accumulatedMessage, State: 1, } - utils.SendPrivateMessageSSE(userinfo.UserID, messageSSE) + utils.SendPrivateMessageSSE(userinfo.UserID, messageSSE, promptstr) } } } else { - utils.SendGroupMessage(userinfo.GroupID, userinfo.UserID, accumulatedMessage, selfid) + utils.SendGroupMessage(userinfo.GroupID, userinfo.UserID, accumulatedMessage, selfid, promptstr) } ClearMessage(conversationid) diff --git a/applogic/memory.go b/applogic/memory.go index ce6ece5..832f965 100644 --- a/applogic/memory.go +++ b/applogic/memory.go @@ -81,7 +81,7 @@ func GetMemoryTitle(msg string) string { } // 保存记忆 -func (app *App) handleSaveMemory(msg structs.OnebotGroupMessage, ConversationID string, ParentMessageID string) { +func (app *App) handleSaveMemory(msg structs.OnebotGroupMessage, ConversationID string, ParentMessageID string, promptstr string) { conversationTitle := "2024-5-19/18:26" // 默认标题,根据实际需求可能需要调整为动态生成的时间戳 userid := msg.UserID @@ -124,11 +124,11 @@ func (app *App) handleSaveMemory(msg structs.OnebotGroupMessage, ConversationID // 发送保存成功的响应 saveMemoryResponse := "记忆保存成功!" - app.sendMemoryResponseWithkeyBoard(msg, saveMemoryResponse, keyboard) + app.sendMemoryResponseWithkeyBoard(msg, saveMemoryResponse, keyboard, promptstr) } // 获取记忆列表 -func (app *App) handleMemoryList(msg structs.OnebotGroupMessage) { +func (app *App) handleMemoryList(msg structs.OnebotGroupMessage, promptstr string) { userid := msg.UserID if config.GetGroupContext() && msg.MessageType != "private" { @@ -172,11 +172,11 @@ func (app *App) handleMemoryList(msg structs.OnebotGroupMessage) { responseBuilder.WriteString(fmt.Sprintf("提示:发送 %s 任意标题开头的前n字即可载入记忆\n如:%s %s", loadMemoryCommand, loadMemoryCommand, exampleTitle)) // 发送组合后的信息,包括键盘数组 - app.sendMemoryResponseByline(msg, responseBuilder.String(), keyboard) + app.sendMemoryResponseByline(msg, responseBuilder.String(), keyboard, promptstr) } // 载入记忆 -func (app *App) handleLoadMemory(msg structs.OnebotGroupMessage, checkResetCommand string) { +func (app *App) handleLoadMemory(msg structs.OnebotGroupMessage, checkResetCommand string, promptstr string) { userid := msg.UserID if config.GetGroupContext() && msg.MessageType != "private" { @@ -198,7 +198,7 @@ func (app *App) handleLoadMemory(msg structs.OnebotGroupMessage, checkResetComma memories, err := app.GetUserMemories(userid) if err != nil { log.Printf("Error retrieving memories: %s", err) - app.sendMemoryResponse(msg, "获取记忆失败") + app.sendMemoryResponse(msg, "获取记忆失败", promptstr) return } @@ -212,7 +212,7 @@ func (app *App) handleLoadMemory(msg structs.OnebotGroupMessage, checkResetComma } if matchedMemory == nil { - app.sendMemoryResponse(msg, "未找到匹配的记忆") + app.sendMemoryResponse(msg, "未找到匹配的记忆", promptstr) return } @@ -220,51 +220,51 @@ func (app *App) handleLoadMemory(msg structs.OnebotGroupMessage, checkResetComma err = app.updateUserContextPro(userid, matchedMemory.ConversationID, matchedMemory.ParentMessageID) if err != nil { log.Printf("Error adding memory: %s", err) - app.sendMemoryResponse(msg, "载入记忆失败") + app.sendMemoryResponse(msg, "载入记忆失败", promptstr) return } // 组合回复信息 responseMessage := fmt.Sprintf("成功载入了标题为 '%s' 的记忆", matchedMemory.ConversationTitle) - app.sendMemoryResponse(msg, responseMessage) + app.sendMemoryResponse(msg, responseMessage, promptstr) } -func (app *App) sendMemoryResponseWithkeyBoard(msg structs.OnebotGroupMessage, response string, keyboard []string) { +func (app *App) sendMemoryResponseWithkeyBoard(msg structs.OnebotGroupMessage, response string, keyboard []string, promptstr string) { strSelfID := strconv.FormatInt(msg.SelfID, 10) if msg.RealMessageType == "group_private" || msg.MessageType == "private" { if !config.GetUsePrivateSSE() { - utils.SendPrivateMessage(msg.UserID, response, strSelfID) + utils.SendPrivateMessage(msg.UserID, response, strSelfID, promptstr) } else { - utils.SendSSEPrivateMessageWithKeyboard(msg.UserID, response, keyboard) + utils.SendSSEPrivateMessageWithKeyboard(msg.UserID, response, keyboard, promptstr) } } else { - utils.SendGroupMessage(msg.GroupID, msg.UserID, response, strSelfID) + utils.SendGroupMessage(msg.GroupID, msg.UserID, response, strSelfID, promptstr) } } -func (app *App) sendMemoryResponse(msg structs.OnebotGroupMessage, response string) { +func (app *App) sendMemoryResponse(msg structs.OnebotGroupMessage, response string, promptstr string) { strSelfID := strconv.FormatInt(msg.SelfID, 10) if msg.RealMessageType == "group_private" || msg.MessageType == "private" { if !config.GetUsePrivateSSE() { - utils.SendPrivateMessage(msg.UserID, response, strSelfID) + utils.SendPrivateMessage(msg.UserID, response, strSelfID, promptstr) } else { - utils.SendSSEPrivateMessage(msg.UserID, response) + utils.SendSSEPrivateMessage(msg.UserID, response, promptstr) } } else { - utils.SendGroupMessage(msg.GroupID, msg.UserID, response, strSelfID) + utils.SendGroupMessage(msg.GroupID, msg.UserID, response, strSelfID, promptstr) } } -func (app *App) sendMemoryResponseByline(msg structs.OnebotGroupMessage, response string, keyboard []string) { +func (app *App) sendMemoryResponseByline(msg structs.OnebotGroupMessage, response string, keyboard []string, promptstr string) { strSelfID := strconv.FormatInt(msg.SelfID, 10) if msg.RealMessageType == "group_private" || msg.MessageType == "private" { if !config.GetUsePrivateSSE() { - utils.SendPrivateMessage(msg.UserID, response, strSelfID) + utils.SendPrivateMessage(msg.UserID, response, strSelfID, promptstr) } else { - utils.SendSSEPrivateMessageByLine(msg.UserID, response, keyboard) + utils.SendSSEPrivateMessageByLine(msg.UserID, response, keyboard, promptstr) } } else { - utils.SendGroupMessage(msg.GroupID, msg.UserID, response, strSelfID) + utils.SendGroupMessage(msg.GroupID, msg.UserID, response, strSelfID, promptstr) } } @@ -280,7 +280,7 @@ func formatHistory(history []structs.Message) string { return result } -func (app *App) handleNewConversation(msg structs.OnebotGroupMessage, conversationID string, parentMessageID string) { +func (app *App) handleNewConversation(msg structs.OnebotGroupMessage, conversationID string, parentMessageID string, promotstr string) { // 使用预定义的时间戳作为会话标题 conversationTitle := "2024-5-19/18:26" // 实际应用中应使用动态生成的时间戳 userid := msg.UserID @@ -323,6 +323,6 @@ func (app *App) handleNewConversation(msg structs.OnebotGroupMessage, conversati if len(loadCommand) > 0 { loadMemoryCommand := loadCommand[0] // 使用数组中的第一个指令 saveMemoryResponse := fmt.Sprintf("旧的对话已经保存,可发送 %s 来查看,可以开始新的对话了!", loadMemoryCommand) - app.sendMemoryResponse(msg, saveMemoryResponse) + app.sendMemoryResponse(msg, saveMemoryResponse, promotstr) } } diff --git a/applogic/vectorsensitive.go b/applogic/vectorsensitive.go index e5dc922..090abdc 100644 --- a/applogic/vectorsensitive.go +++ b/applogic/vectorsensitive.go @@ -258,7 +258,7 @@ func (app *App) textExistsInDatabase(text string) (bool, error) { return exists, nil } -func (app *App) InterceptSensitiveContent(vector []float64, message structs.OnebotGroupMessage, selfid string) (int, string, error) { +func (app *App) InterceptSensitiveContent(vector []float64, message structs.OnebotGroupMessage, selfid string, promptstr string) (int, string, error) { // 自定义阈值 Threshold := config.GetVertorSensitiveThreshold() @@ -283,12 +283,12 @@ func (app *App) InterceptSensitiveContent(vector []float64, message structs.Oneb if saveresponse != "" { if message.RealMessageType == "group_private" || message.MessageType == "private" { if !config.GetUsePrivateSSE() { - utils.SendPrivateMessage(message.UserID, saveresponse, selfid) + utils.SendPrivateMessage(message.UserID, saveresponse, selfid, promptstr) } else { - utils.SendSSEPrivateSafeMessage(message.UserID, saveresponse) + utils.SendSSEPrivateSafeMessage(message.UserID, saveresponse, promptstr) } } else { - utils.SendGroupMessage(message.GroupID, message.UserID, saveresponse, selfid) + utils.SendGroupMessage(message.GroupID, message.UserID, saveresponse, selfid, promptstr) } return 1, saveresponse, nil } diff --git a/config/config.go b/config/config.go index f91c204..5da50f7 100644 --- a/config/config.go +++ b/config/config.go @@ -3015,76 +3015,6 @@ func getTemperatureHunyuanInternal(options ...string) float64 { return value } -// // 获取助手ID -// func GetYuanqiAssistantID(options ...string) string { -// mu.Lock() -// defer mu.Unlock() -// return getYuanqiAssistantIDInternal(options...) -// } - -// func getYuanqiAssistantIDInternal(options ...string) string { -// if len(options) == 0 || options[0] == "" { -// if instance != nil { -// return instance.Settings.YuanqiAssistantID -// } -// return "" // 默认值或错误处理 -// } - -// basename := options[0] -// assistantIDInterface, err := prompt.GetSettingFromFilename(basename, "YuanqiAssistantID") -// if err != nil { -// log.Println("Error retrieving YuanqiAssistantID:", err) -// return getYuanqiAssistantIDInternal() // 递归调用内部函数,不传递任何参数 -// } - -// assistantID, ok := assistantIDInterface.(string) -// if !ok { // 检查类型断言是否失败 -// log.Println("Type assertion failed for YuanqiAssistantID, fetching default") -// return getYuanqiAssistantIDInternal() // 递归调用内部函数,不传递任何参数 -// } - -// if assistantID == "" { -// return getYuanqiAssistantIDInternal() // 递归调用内部函数,不传递任何参数 -// } - -// return assistantID -// } - -// // 获取Token -// func GetYuanqiToken(options ...string) string { -// mu.Lock() -// defer mu.Unlock() -// return getYuanqiTokenInternal(options...) -// } - -// func getYuanqiTokenInternal(options ...string) string { -// if len(options) == 0 || options[0] == "" { -// if instance != nil { -// return instance.Settings.YuanqiToken -// } -// return "" // 默认值或错误处理 -// } - -// basename := options[0] -// YuanqiTokenInterface, err := prompt.GetSettingFromFilename(basename, "YuanqiToken") -// if err != nil { -// log.Println("Error retrieving YuanqiToken:", err) -// return getYuanqiTokenInternal() // 递归调用内部函数,不传递任何参数 -// } - -// YuanqiToken, ok := YuanqiTokenInterface.(string) -// if !ok { // 检查类型断言是否失败 -// log.Println("Type assertion failed for YuanqiToken, fetching default") -// return getYuanqiTokenInternal() // 递归调用内部函数,不传递任何参数 -// } - -// if YuanqiToken == "" { -// return getYuanqiTokenInternal() // 递归调用内部函数,不传递任何参数 -// } - -// return YuanqiToken -// } - // GetYuanqiConf return conf.YuanqiAssistantID, conf.YuanqiToken func GetYuanqiConf(options ...string) (string, string) { mu.Lock() @@ -3102,9 +3032,6 @@ func getYuanqiConfInternal(options ...string) (string, string) { conf := instance.Settings.Yuanqiconfs[index] return conf.YuanqiAssistantID, conf.YuanqiToken } - } else { - log.Println("No configurations available in instance or default behavior") - return "", "" // 默认值或错误处理 } // 使用prompt包从指定的文件名中获取配置 @@ -3133,6 +3060,84 @@ func getYuanqiConfInternal(options ...string) (string, string) { return conf.YuanqiAssistantID, conf.YuanqiToken } +// 获取 ReplacementPairsIn +func GetReplacementPairsIn(options ...string) []structs.ReplacementPair { + mu.Lock() + defer mu.Unlock() + return getReplacementPairsInInternal(options...) +} + +// getReplacementPairsInInternal 内部递归函数,处理配置获取逻辑 +func getReplacementPairsInInternal(options ...string) []structs.ReplacementPair { + // 从instance中读取配置数组 + if instance != nil && len(instance.Settings.ReplacementPairsIn) > 0 { + if len(options) == 0 || options[0] == "" { + return instance.Settings.ReplacementPairsIn + } + } + + // 使用prompt包从指定的文件名中获取配置 + basename := options[0] + confInterface, err := prompt.GetSettingFromFilename(basename, "ReplacementPairsIn") + if err != nil { + log.Printf("Error retrieving settings from file: %s, error: %v", basename, err) + return getReplacementPairsInInternal() // 递归调用内部函数,不传递任何参数 + } + + // 断言获取的interface{}为[]ReplacementPair + confs, ok := confInterface.([]structs.ReplacementPair) + if !ok { + log.Println("Type assertion failed for ReplacementPair, attempting default behavior") + return getReplacementPairsInInternal() // 递归调用内部函数,尝试默认配置 + } + + if len(confs) == 0 { + log.Println("No configurations found in file:", basename) + return getReplacementPairsInInternal() // 递归调用内部函数,尝试默认配置 + } + + return confs +} + +// 获取 ReplacementPairsOut +func GetReplacementPairsOut(options ...string) []structs.ReplacementPair { + mu.Lock() + defer mu.Unlock() + return getReplacementPairsOutInternal(options...) +} + +// getReplacementPairsInOutternal 内部递归函数,处理配置获取逻辑 +func getReplacementPairsOutInternal(options ...string) []structs.ReplacementPair { + // 从instance中读取配置数组 + if instance != nil && len(instance.Settings.ReplacementPairsOut) > 0 { + if len(options) == 0 || options[0] == "" { + return instance.Settings.ReplacementPairsOut + } + } + + // 使用prompt包从指定的文件名中获取配置 + basename := options[0] + confInterface, err := prompt.GetSettingFromFilename(basename, "ReplacementPairsOut") + if err != nil { + log.Printf("Error retrieving settings from file: %s, error: %v", basename, err) + return getReplacementPairsOutInternal() // 递归调用内部函数,不传递任何参数 + } + + // 断言获取的interface{}为[]ReplacementPair + confs, ok := confInterface.([]structs.ReplacementPair) + if !ok { + log.Println("Type assertion failed for ReplacementPair, attempting default behavior") + return getReplacementPairsOutInternal() // 递归调用内部函数,尝试默认配置 + } + + if len(confs) == 0 { + log.Println("No configurations found in file:", basename) + return getReplacementPairsOutInternal() // 递归调用内部函数,尝试默认配置 + } + + return confs +} + // 获取助手版本 func GetYuanqiVersion(options ...string) float64 { mu.Lock() diff --git a/structs/struct.go b/structs/struct.go index 41cfa19..4b3514c 100644 --- a/structs/struct.go +++ b/structs/struct.go @@ -245,27 +245,35 @@ type WXFunctionCall struct { Thought string `json:"thought,omitempty"` } +// ReplacementPair 表示一对替换词,其中包含原始词和目标替换词。 +type ReplacementPair struct { + OriginalWord string `yaml:"originalWord"` + TargetWord string `yaml:"targetWord"` +} + type Settings struct { - AllApi bool `yaml:"allApi"` - SecretId string `yaml:"secretId"` - SecretKey string `yaml:"secretKey"` - Region string `yaml:"region"` - UseSse bool `yaml:"useSse"` - Port int `yaml:"port"` - SelfPath string `yaml:"selfPath"` - HttpPath string `yaml:"path"` - HttpPaths []string `yaml:"paths"` - Lotus string `yaml:"lotus"` - PathToken string `yaml:"pathToken"` - SystemPrompt []string `yaml:"systemPrompt"` - IPWhiteList []string `yaml:"iPWhiteList"` - ApiType int `yaml:"apiType"` - Proxy string `yaml:"proxy"` - UrlSendPics bool `yaml:"urlSendPics"` // 自己构造图床加速图片发送 - MdPromptKeyboardAtGroup bool `yaml:"mdPromptKeyboardAtGroup"` // 群内使用md能力模拟PromptKeyboard - GroupHintWords []string `yaml:"groupHintWords"` - GroupHintChance int `yaml:"groupHintChance"` - GroupContext bool `yaml:"groupContext"` + AllApi bool `yaml:"allApi"` + SecretId string `yaml:"secretId"` + SecretKey string `yaml:"secretKey"` + Region string `yaml:"region"` + UseSse bool `yaml:"useSse"` + Port int `yaml:"port"` + SelfPath string `yaml:"selfPath"` + HttpPath string `yaml:"path"` + HttpPaths []string `yaml:"paths"` + Lotus string `yaml:"lotus"` + PathToken string `yaml:"pathToken"` + SystemPrompt []string `yaml:"systemPrompt"` + IPWhiteList []string `yaml:"iPWhiteList"` + ApiType int `yaml:"apiType"` + Proxy string `yaml:"proxy"` + UrlSendPics bool `yaml:"urlSendPics"` // 自己构造图床加速图片发送 + MdPromptKeyboardAtGroup bool `yaml:"mdPromptKeyboardAtGroup"` // 群内使用md能力模拟PromptKeyboard + GroupHintWords []string `yaml:"groupHintWords"` + GroupHintChance int `yaml:"groupHintChance"` + GroupContext bool `yaml:"groupContext"` + ReplacementPairsIn []ReplacementPair `yaml:"replacementPairsIn"` + ReplacementPairsOut []ReplacementPair `yaml:"replacementPairsOut"` HunyuanType int `yaml:"hunyuanType"` MaxTokensHunyuan int `yaml:"maxTokensHunyuan"` diff --git a/template/config_template.go b/template/config_template.go index 08ade01..2a1d2d2 100644 --- a/template/config_template.go +++ b/template/config_template.go @@ -51,6 +51,12 @@ settings: groupHintWords : [] #当机器人位于群内时,需满足包含groupHintWords数组任意内容如[CQ:at,qq=2] 机器人的名字 等 groupHintChance : 0 #需与groupHintWords联用,代表不满足hintwords时概率触发,不启用groupHintWords相当于百分百概率回复. groupContext : false #群上下文 在智能体在群内时,以群为单位处理上下文. + replacementPairsIn: #每个不同的yml文件,都可以有自己独立的替换词规则,IN OUT分离 + - originalWord: "hello" + targetWord: "hi" + replacementPairsOut: #每个不同的yml文件,都可以有自己独立的替换词规则,IN OUT分离 + - originalWord: "hello" + targetWord: "hi" #Ws服务器配置 wsServerToken : "" #ws密钥 可以由onebotv11反向ws接入 diff --git a/utils/blacklist.go b/utils/blacklist.go index 77b1aea..f8eaf70 100644 --- a/utils/blacklist.go +++ b/utils/blacklist.go @@ -93,7 +93,7 @@ func WatchBlacklist(filePath string) { } // BlacklistIntercept 检查用户ID是否在黑名单中,如果在,则发送预设消息 -func BlacklistIntercept(message structs.OnebotGroupMessage, selfid string) bool { +func BlacklistIntercept(message structs.OnebotGroupMessage, selfid string, promptstr string) bool { // 检查群ID是否在黑名单中 if IsInBlacklist(strconv.FormatInt(message.GroupID, 10)) { // 获取黑名单响应消息 @@ -102,12 +102,12 @@ func BlacklistIntercept(message structs.OnebotGroupMessage, selfid string) bool // 根据消息类型发送响应 if message.RealMessageType == "group_private" || message.MessageType == "private" { if !config.GetUsePrivateSSE() { - SendPrivateMessage(message.UserID, responseMessage, selfid) + SendPrivateMessage(message.UserID, responseMessage, selfid, promptstr) } else { - SendSSEPrivateMessage(message.UserID, responseMessage) + SendSSEPrivateMessage(message.UserID, responseMessage, promptstr) } } else { - SendGroupMessage(message.GroupID, message.UserID, responseMessage, selfid) + SendGroupMessage(message.GroupID, message.UserID, responseMessage, selfid, promptstr) } fmt.Printf("groupid:[%v]这个群在黑名单中,被拦截\n", message.GroupID) @@ -122,12 +122,12 @@ func BlacklistIntercept(message structs.OnebotGroupMessage, selfid string) bool // 根据消息类型发送响应 if message.RealMessageType == "group_private" || message.MessageType == "private" { if !config.GetUsePrivateSSE() { - SendPrivateMessage(message.UserID, responseMessage, selfid) + SendPrivateMessage(message.UserID, responseMessage, selfid, promptstr) } else { - SendSSEPrivateMessage(message.UserID, responseMessage) + SendSSEPrivateMessage(message.UserID, responseMessage, promptstr) } } else { - SendGroupMessage(message.GroupID, message.UserID, responseMessage, selfid) + SendGroupMessage(message.GroupID, message.UserID, responseMessage, selfid, promptstr) } fmt.Printf("userid:[%v]这位用户在黑名单中,被拦截\n", message.UserID) diff --git a/utils/utils.go b/utils/utils.go index 8022efc..a3b8e1b 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -207,7 +207,7 @@ func ExtractEventDetails(eventData map[string]interface{}) (string, structs.Usag return responseTextBuilder.String(), totalUsage } -func SendGroupMessage(groupID int64, userID int64, message string, selfid string) error { +func SendGroupMessage(groupID int64, userID int64, message string, selfid string, promptstr string) error { //TODO: 用userid作为了echo,在ws收到回调信息的时候,加入到全局撤回数组,AddMessageID,实现撤回 if server.IsSelfIDExists(selfid) { // 创建消息结构体 @@ -254,6 +254,9 @@ func SendGroupMessage(groupID int64, userID int64, message string, selfid string message = acnode.CheckWordOUT(message) } + //精细化替换 每个yml配置文件都可以具有一个非全局的文本替换规则 + message = ReplaceTextOut(message, promptstr) + // 去除末尾的换行符 不去除会导致不好看 message = removeTrailingCRLFs(message) @@ -351,6 +354,9 @@ func SendGroupMessageMdPromptKeyboard(groupID int64, userID int64, message strin message = acnode.CheckWordOUT(message) } + //精细化替换 每个yml配置文件都可以具有一个非全局的文本替换规则 + message = ReplaceTextOut(message, promptstr) + // 去除末尾的换行符 不去除会导致不好看 message = removeTrailingCRLFs(message) @@ -548,7 +554,7 @@ func SendGroupMessageMdPromptKeyboard(groupID int64, userID int64, message strin return nil } -func SendPrivateMessage(UserID int64, message string, selfid string) error { +func SendPrivateMessage(UserID int64, message string, selfid string, promptstr string) error { if server.IsSelfIDExists(selfid) { // 创建消息结构体 msg := map[string]interface{}{ @@ -593,6 +599,9 @@ func SendPrivateMessage(UserID int64, message string, selfid string) error { message = acnode.CheckWordOUT(message) } + //精细化替换 每个yml配置文件都可以具有一个非全局的文本替换规则 + message = ReplaceTextOut(message, promptstr) + // 去除末尾的换行符 不去除会导致不好看 message = removeTrailingCRLFs(message) @@ -732,7 +741,7 @@ func SendPrivateMessageRaw(UserID int64, message string, selfid string) error { return nil } -func SendPrivateMessageSSE(UserID int64, message structs.InterfaceBody) error { +func SendPrivateMessageSSE(UserID int64, message structs.InterfaceBody, promptstr string) error { // 获取基础URL baseURL := config.GetHttpPath() // 假设config.GetHttpPath()返回基础URL @@ -764,6 +773,9 @@ func SendPrivateMessageSSE(UserID int64, message structs.InterfaceBody) error { message.Content = acnode.CheckWordOUT(message.Content) } + //精细化替换 每个yml配置文件都可以具有一个非全局的文本替换规则 + message.Content = ReplaceTextOut(message.Content, promptstr) + // 调试用的 if config.GetPrintHanming() { fmtf.Printf("流式信息替换后:%v", message.Content) @@ -943,7 +955,7 @@ func PostSensitiveMessages() error { } // SendSSEPrivateMessage 分割并发送消息的核心逻辑,直接遍历字符串 -func SendSSEPrivateMessage(userID int64, content string) { +func SendSSEPrivateMessage(userID int64, content string, promptstr string) { punctuations := []rune{'。', '!', '?', ',', ',', '.', '!', '?', '~'} splitProbability := config.GetSplitByPuntuations() @@ -995,12 +1007,12 @@ func SendSSEPrivateMessage(userID int64, content string) { } // 发送SSE消息函数 - SendPrivateMessageSSE(userID, messageSSE) + SendPrivateMessageSSE(userID, messageSSE, promptstr) } } // SendSSEPrivateMessageWithKeyboard 分割并发送消息的核心逻辑,直接遍历字符串 -func SendSSEPrivateMessageWithKeyboard(userID int64, content string, keyboard []string) { +func SendSSEPrivateMessageWithKeyboard(userID int64, content string, keyboard []string, promptstr string) { punctuations := []rune{'。', '!', '?', ',', ',', '.', '!', '?', '~'} splitProbability := config.GetSplitByPuntuations() @@ -1057,12 +1069,12 @@ func SendSSEPrivateMessageWithKeyboard(userID int64, content string, keyboard [] } // 发送SSE消息函数 - SendPrivateMessageSSE(userID, messageSSE) + SendPrivateMessageSSE(userID, messageSSE, promptstr) } } // SendSSEPrivateMessageByline 分割并发送消息的核心逻辑,直接遍历字符串 -func SendSSEPrivateMessageByLine(userID int64, content string, keyboard []string) { +func SendSSEPrivateMessageByLine(userID int64, content string, keyboard []string, promptstr string) { // 直接使用 strings.Split 按行分割字符串 parts := strings.Split(content, "\n") @@ -1105,12 +1117,12 @@ func SendSSEPrivateMessageByLine(userID int64, content string, keyboard []string } // 发送SSE消息函数 - SendPrivateMessageSSE(userID, messageSSE) + SendPrivateMessageSSE(userID, messageSSE, promptstr) } } // SendSSEPrivateSafeMessage 分割并发送安全消息的核心逻辑,直接遍历字符串 -func SendSSEPrivateSafeMessage(userID int64, saveresponse string) { +func SendSSEPrivateSafeMessage(userID int64, saveresponse string, promptstr string) { // 将字符串转换为rune切片,以正确处理多字节字符 runes := []rune(saveresponse) @@ -1137,14 +1149,14 @@ func SendSSEPrivateSafeMessage(userID int64, saveresponse string) { State: 1, } - SendPrivateMessageSSE(userID, messageSSE) + SendPrivateMessageSSE(userID, messageSSE, promptstr) // 中间 messageSSE = structs.InterfaceBody{ Content: parts[1], State: 11, } - SendPrivateMessageSSE(userID, messageSSE) + SendPrivateMessageSSE(userID, messageSSE, promptstr) // 从配置中获取恢复响应数组 RestoreResponses := config.GetRestoreCommand() @@ -1172,11 +1184,11 @@ func SendSSEPrivateSafeMessage(userID int64, saveresponse string) { } // 发送SSE私人消息 - SendPrivateMessageSSE(userID, messageSSE) + SendPrivateMessageSSE(userID, messageSSE, promptstr) } // SendSSEPrivateRestoreMessage 分割并发送重置消息的核心逻辑,直接遍历字符串 -func SendSSEPrivateRestoreMessage(userID int64, RestoreResponse string) { +func SendSSEPrivateRestoreMessage(userID int64, RestoreResponse string, promptstr string) { // 将字符串转换为rune切片,以正确处理多字节字符 runes := []rune(RestoreResponse) @@ -1204,14 +1216,14 @@ func SendSSEPrivateRestoreMessage(userID int64, RestoreResponse string) { State: 1, } - SendPrivateMessageSSE(userID, messageSSE) + SendPrivateMessageSSE(userID, messageSSE, promptstr) //中间 messageSSE = structs.InterfaceBody{ Content: parts[1], State: 11, } - SendPrivateMessageSSE(userID, messageSSE) + SendPrivateMessageSSE(userID, messageSSE, promptstr) // 从配置中获取promptkeyboard promptkeyboard := config.GetPromptkeyboard() @@ -1224,11 +1236,11 @@ func SendSSEPrivateRestoreMessage(userID int64, RestoreResponse string) { } // 发送SSE私人消息 - SendPrivateMessageSSE(userID, messageSSE) + SendPrivateMessageSSE(userID, messageSSE, promptstr) } // LanguageIntercept 检查文本语言,如果不在允许列表中,则返回 true 并发送消息 -func LanguageIntercept(text string, message structs.OnebotGroupMessage, selfid string) bool { +func LanguageIntercept(text string, message structs.OnebotGroupMessage, selfid string, promptstr string) bool { hintWords := config.GetGroupHintWords() // 遍历所有触发词,将其从文本中剔除 for _, word := range hintWords { @@ -1253,12 +1265,12 @@ func LanguageIntercept(text string, message structs.OnebotGroupMessage, selfid s // 发送响应消息 if message.RealMessageType == "group_private" || message.MessageType == "private" { if !config.GetUsePrivateSSE() { - SendPrivateMessage(message.UserID, responseMessage, selfid) + SendPrivateMessage(message.UserID, responseMessage, selfid, promptstr) } else { - SendSSEPrivateMessage(message.UserID, responseMessage) + SendSSEPrivateMessage(message.UserID, responseMessage, promptstr) } } else { - SendGroupMessage(message.GroupID, message.UserID, responseMessage, selfid) + SendGroupMessage(message.GroupID, message.UserID, responseMessage, selfid, promptstr) } return true // 拦截 @@ -1301,7 +1313,7 @@ func FriendlyLanguageNameCN(lang whatlanggo.Lang) string { } // LengthIntercept 检查文本长度,如果超过最大长度,则返回 true 并发送消息 -func LengthIntercept(text string, message structs.OnebotGroupMessage, selfid string) bool { +func LengthIntercept(text string, message structs.OnebotGroupMessage, selfid string, promptstr string) bool { maxLen := config.GetQuestionMaxLenth() if len(text) > maxLen { // 长度超出限制,获取并发送响应消息 @@ -1310,12 +1322,12 @@ func LengthIntercept(text string, message structs.OnebotGroupMessage, selfid str // 根据消息类型发送响应 if message.RealMessageType == "group_private" || message.MessageType == "private" { if !config.GetUsePrivateSSE() { - SendPrivateMessage(message.UserID, responseMessage, selfid) + SendPrivateMessage(message.UserID, responseMessage, selfid, promptstr) } else { - SendSSEPrivateMessage(message.UserID, responseMessage) + SendSSEPrivateMessage(message.UserID, responseMessage, promptstr) } } else { - SendGroupMessage(message.GroupID, message.UserID, responseMessage, selfid) + SendGroupMessage(message.GroupID, message.UserID, responseMessage, selfid, promptstr) } return true // 拦截 @@ -1486,3 +1498,45 @@ func MakeAlternating(messages []structs.MessageContent) []structs.MessageContent return correctedMessages } + +// ReplaceTextIn 使用给定的替换对列表对文本进行替换 +func ReplaceTextIn(text string, promptstr string) string { + // 调用 GetReplacementPairsIn 函数获取替换对列表 + replacementPairs := config.GetReplacementPairsIn(promptstr) + + fmt.Printf("测试测试:%+v,%v", replacementPairs, promptstr) + + if len(replacementPairs) == 0 { + return text + } + + // 遍历所有的替换对,并在文本中进行替换 + for _, pair := range replacementPairs { + // 使用 strings.Replace 替换文本中的所有出现 + // 注意这里我们使用 -1 作为最后的参数,表示替换文本中的所有匹配项 + text = strings.Replace(text, pair.OriginalWord, pair.TargetWord, -1) + } + + // 返回替换后的文本 + return text +} + +// ReplaceTextOut 使用给定的替换对列表对文本进行替换 +func ReplaceTextOut(text string, promptstr string) string { + // 调用 GetReplacementPairsIn 函数获取替换对列表 + replacementPairs := config.GetReplacementPairsOut(promptstr) + + if len(replacementPairs) == 0 { + return text + } + + // 遍历所有的替换对,并在文本中进行替换 + for _, pair := range replacementPairs { + // 使用 strings.Replace 替换文本中的所有出现 + // 注意这里我们使用 -1 作为最后的参数,表示替换文本中的所有匹配项 + text = strings.Replace(text, pair.OriginalWord, pair.TargetWord, -1) + } + + // 返回替换后的文本 + return text +} From ff1b31d1547d13c30958ceffd535670d4b5cec54 Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Thu, 6 Jun 2024 14:53:07 +0800 Subject: [PATCH 26/30] Beta139 (#138) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 * beta117 * beta118 * beta119 * beta120 * beta121 * beta122 * beta123 * beta124 * beta125 * beta126 * beta127 * beta128 * beta129 * beta130 * beta131 * beta132 * beta1333 * beta134 * beta135 * beta136 * beta137 * beta138 * beta139 --- config/config.go | 20 +++++++++++++------- utils/utils.go | 2 -- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/config/config.go b/config/config.go index 5da50f7..acc23bd 100644 --- a/config/config.go +++ b/config/config.go @@ -3024,13 +3024,15 @@ func GetYuanqiConf(options ...string) (string, string) { // getYuanqiConfInternal 内部递归函数,处理配置获取逻辑 func getYuanqiConfInternal(options ...string) (string, string) { - // 从instance中读取配置数组 - if instance != nil && len(instance.Settings.Yuanqiconfs) > 0 { - if len(options) == 0 || options[0] == "" { + if len(options) == 0 || options[0] == "" { + // 从instance中读取配置数组 + if instance != nil && len(instance.Settings.Yuanqiconfs) > 0 { // 从instance全局变量中随机选择一个配置 index := rand.Intn(len(instance.Settings.Yuanqiconfs)) conf := instance.Settings.Yuanqiconfs[index] return conf.YuanqiAssistantID, conf.YuanqiToken + } else { + return "", "" } } @@ -3070,9 +3072,11 @@ func GetReplacementPairsIn(options ...string) []structs.ReplacementPair { // getReplacementPairsInInternal 内部递归函数,处理配置获取逻辑 func getReplacementPairsInInternal(options ...string) []structs.ReplacementPair { // 从instance中读取配置数组 - if instance != nil && len(instance.Settings.ReplacementPairsIn) > 0 { - if len(options) == 0 || options[0] == "" { + if len(options) == 0 || options[0] == "" { + if instance != nil && len(instance.Settings.ReplacementPairsIn) > 0 { return instance.Settings.ReplacementPairsIn + } else { + return nil } } @@ -3109,9 +3113,11 @@ func GetReplacementPairsOut(options ...string) []structs.ReplacementPair { // getReplacementPairsInOutternal 内部递归函数,处理配置获取逻辑 func getReplacementPairsOutInternal(options ...string) []structs.ReplacementPair { // 从instance中读取配置数组 - if instance != nil && len(instance.Settings.ReplacementPairsOut) > 0 { - if len(options) == 0 || options[0] == "" { + if len(options) == 0 || options[0] == "" { + if instance != nil && len(instance.Settings.ReplacementPairsOut) > 0 { return instance.Settings.ReplacementPairsOut + } else { + return nil } } diff --git a/utils/utils.go b/utils/utils.go index a3b8e1b..4b1c2ba 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -1504,8 +1504,6 @@ func ReplaceTextIn(text string, promptstr string) string { // 调用 GetReplacementPairsIn 函数获取替换对列表 replacementPairs := config.GetReplacementPairsIn(promptstr) - fmt.Printf("测试测试:%+v,%v", replacementPairs, promptstr) - if len(replacementPairs) == 0 { return text } From dfcfc4f470ee5d407da284d53396767ca0aeac03 Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Thu, 6 Jun 2024 20:14:11 +0800 Subject: [PATCH 27/30] Beta140 (#139) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 * beta117 * beta118 * beta119 * beta120 * beta121 * beta122 * beta123 * beta124 * beta125 * beta126 * beta127 * beta128 * beta129 * beta130 * beta131 * beta132 * beta1333 * beta134 * beta135 * beta136 * beta137 * beta138 * beta139 * beta140 --- applogic/gensokyo.go | 55 ++++++++++++++++++++++++++++---------------- utils/utils.go | 49 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 20 deletions(-) diff --git a/applogic/gensokyo.go b/applogic/gensokyo.go index cd5bd86..8cd2e2e 100644 --- a/applogic/gensokyo.go +++ b/applogic/gensokyo.go @@ -80,12 +80,17 @@ func ResetAllIndexes() { } // checkMessageForHints 检查消息中是否包含给定的提示词 -func checkMessageForHints(message string) bool { +func checkMessageForHints(message string, selfid int64, promptstr string) bool { // 从配置中获取提示词数组 - hintWords := config.GetGroupHintWords() + hintWords := config.GetGroupHintWords(promptstr) if len(hintWords) == 0 { return true // 未设置,直接返回0 } + + selfidstr := strconv.FormatInt(selfid, 10) + // 使用[@+selfid+]格式,向提示词数组增加一个成员 + hintWords = append(hintWords, "[@"+selfidstr+"]") + // 遍历每个提示词,检查它们是否出现在消息中 for _, hint := range hintWords { if strings.Contains(message, hint) { @@ -135,9 +140,19 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { return } + var promptstr string + // 读取URL参数 "prompt" + promptstr = r.URL.Query().Get("prompt") + if promptstr != "" { + // 使用 prompt 变量进行后续处理 + fmt.Printf("收到prompt参数: %s\n", promptstr) + } + // 判断是否是群聊,然后检查触发词 if message.RealMessageType != "group_private" && message.MessageType != "private" { - if !checkMessageForHints(utils.RemoveBracketsContent(message.RawMessage)) { + // 去除含2个[[]]的内容 + checkstr := utils.RemoveBracketsContent(message.RawMessage) + if !checkMessageForHints(checkstr, message.SelfID, promptstr) { // 获取概率值 chance := config.GetGroupHintChance() @@ -153,6 +168,8 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // 记录日志,表明概率检查通过 fmt.Printf("Probability check passed: %d%% chance, random value: %d\n", chance, randomValue) } + } else { + fmt.Printf("checkMessageForHints check passed") } } @@ -171,17 +188,10 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } } - var promptstr string if CustomRecord != nil { // 提示词参数 - if CustomRecord.PromptStr == "" { - // 读取URL参数 "prompt" - promptstr = r.URL.Query().Get("prompt") - if promptstr != "" { - // 使用 prompt 变量进行后续处理 - fmt.Printf("收到prompt参数: %s\n", promptstr) - } - } else { + if CustomRecord.PromptStr != "" { + // 用数据库读取到的CustomRecord PromptStr去覆盖当前的PromptStr promptstr = CustomRecord.PromptStr fmt.Printf("刷新prompt参数: %s,newPromptStrStat:%d\n", promptstr, CustomRecord.PromptStrStat-1) newPromptStrStat := CustomRecord.PromptStrStat - 1 @@ -197,7 +207,6 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { fmt.Printf("app.InsertCustomTableRecord 出错: %s\n", err) } } - } // MARK: 提示词之间 整体切换Q @@ -235,13 +244,6 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } } } else { - // 读取URL参数 "prompt" - promptstr = r.URL.Query().Get("prompt") - if promptstr != "" { - // 使用 prompt 变量进行后续处理 - fmt.Printf("收到prompt参数: %s\n", promptstr) - } - // MARK: 提示词之间 整体切换Q 当用户没有存档时 if config.GetGroupContext() && message.MessageType != "private" { app.ProcessPromptMarks(message.GroupID, message.Message.(string), &promptstr) @@ -330,6 +332,9 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { checkResetCommand = utils.RemoveBracketsContent(checkResetCommand) } + // 去除at自己的 CQ码,如果不是指向自己的,则不响应 + checkResetCommand = utils.RemoveAtTagContentConditional(checkResetCommand, message.SelfID) + // 检查checkResetCommand是否在restoreCommands列表中 isResetCommand := false for _, command := range restoreCommands { @@ -690,6 +695,16 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // 按提示词区分的细化替换 这里主要不是为了安全和敏感词,而是细化效果,也就没有使用acnode提高效率 requestmsg = utils.ReplaceTextIn(requestmsg, promptstr) + // 去除不是针对自己的at CQ码 不响应目标不是自己的at信息 + requestmsg = utils.RemoveAtTagContentConditional(requestmsg, message.SelfID) + if requestmsg == "" { + fmtf.Printf("requestmsg is empty") + // 发送响应 + w.WriteHeader(http.StatusOK) + w.Write([]byte("requestmsg is empty")) + return + } + if config.GetGroupContext() && message.MessageType != "private" { fmtf.Printf("实际请求conversation端点内容:[%v]%v\n", message.GroupID, requestmsg) } else { diff --git a/utils/utils.go b/utils/utils.go index 4b1c2ba..b02ad95 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -887,6 +887,55 @@ func RemoveBracketsContent(input string) string { return re.ReplaceAllString(input, "") } +// RemoveAtTagContent 接收一个字符串,并移除所有[@任意字符]的内容 +func RemoveAtTagContent(input string) string { + // 编译一个正则表达式,用于匹配[@任意字符]的模式 + re := regexp.MustCompile(`\[@.*?\]`) + // 使用正则表达式的ReplaceAllString方法删除匹配的部分 + cleaned := re.ReplaceAllString(input, "") + // 去除前后的空格 + cleaned = strings.TrimSpace(cleaned) + return cleaned +} + +// RemoveAtTagContentConditional 接收一个字符串和一个 int64 类型的 selfid, +// 并根据条件移除[@selfowd]格式的内容,然后去除前后的空格。 +// 只有当标签中的ID与传入的selfid相匹配时才进行移除, +// 如果没有任何[@xxx]出现或者所有出现的[@xxx]中的xxx都不等于selfid时,返回原字符串。 +func RemoveAtTagContentConditional(input string, selfid int64) string { + // 将 int64 类型的 selfid 转换为字符串 + selfidStr := strconv.FormatInt(selfid, 10) + + // 编译一个正则表达式,用于匹配并捕获[@任意字符]的模式 + re := regexp.MustCompile(`\[@(.*?)\]`) + matches := re.FindAllStringSubmatch(input, -1) + + // 如果没有找到任何匹配项,直接返回原输入 + if len(matches) == 0 { + return input + } + + foundSelfId := false // 用于跟踪是否找到与selfid相匹配的标签 + + // 遍历所有匹配项 + for _, match := range matches { + if match[1] == selfidStr { + // 如果找到与selfid相匹配的标签,替换该标签 + input = strings.Replace(input, match[0], "", -1) + foundSelfId = true // 标记已找到至少一个与selfid相匹配的标签 + } + } + + if !foundSelfId { + // 如果没有找到任何与selfid相匹配的标签,将输入置为空 + input = "" + } + + // 去除前后的空格 + cleaned := strings.TrimSpace(input) + return cleaned +} + func PostSensitiveMessages() error { port := config.GetPort() // 从config包获取端口号 var portStr string From 98e105f2537f5a49bcfe4f8a4dbd7095e96cd04d Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Thu, 6 Jun 2024 20:38:28 +0800 Subject: [PATCH 28/30] Beta141 (#140) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 * beta117 * beta118 * beta119 * beta120 * beta121 * beta122 * beta123 * beta124 * beta125 * beta126 * beta127 * beta128 * beta129 * beta130 * beta131 * beta132 * beta1333 * beta134 * beta135 * beta136 * beta137 * beta138 * beta139 * beta140 * beta141 --- applogic/flow.go | 16 ++++++++-------- applogic/gensokyo.go | 26 +++++++++++++------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/applogic/flow.go b/applogic/flow.go index 99a5d05..4488065 100644 --- a/applogic/flow.go +++ b/applogic/flow.go @@ -16,7 +16,7 @@ import ( func (app *App) ApplyPromptChoiceQ(promptstr string, requestmsg *string, message *structs.OnebotGroupMessage) { userid := message.UserID if config.GetGroupContext() && message.MessageType != "private" { - userid = message.GroupID + userid = message.GroupID + message.SelfID } // 检查是否启用了EnhancedQA @@ -139,7 +139,7 @@ func (app *App) ApplyPromptChoiceQ(promptstr string, requestmsg *string, message func (app *App) ApplyPromptCoverQ(promptstr string, requestmsg *string, message *structs.OnebotGroupMessage) { userid := message.UserID if config.GetGroupContext() && message.MessageType != "private" { - userid = message.GroupID + userid = message.GroupID + message.SelfID } // 检查是否启用了EnhancedQA @@ -251,7 +251,7 @@ func (app *App) ApplyPromptCoverQ(promptstr string, requestmsg *string, message func (app *App) ApplySwitchOnQ(promptstr *string, requestmsg *string, message *structs.OnebotGroupMessage) { userid := message.UserID if config.GetGroupContext() && message.MessageType != "private" { - userid = message.GroupID + userid = message.GroupID + message.SelfID } // promptstr 随 switchOnQ 变化 @@ -357,7 +357,7 @@ func (app *App) ProcessExitChoicesQ(promptstr string, requestmsg *string, messag userid := message.UserID if config.GetGroupContext() && message.MessageType != "private" { - userid = message.GroupID + userid = message.GroupID + message.SelfID } // 获取用户剧情存档中的当前状态 @@ -434,7 +434,7 @@ func (app *App) ProcessExitChoicesQ(promptstr string, requestmsg *string, messag func (app *App) HandleExit(exitText string, message *structs.OnebotGroupMessage, selfid string, promptstr string) { userid := message.UserID if config.GetGroupContext() && message.MessageType != "private" { - userid = message.GroupID + userid = message.GroupID + message.SelfID } fmt.Printf("处理重置操作on:%v", exitText) @@ -461,7 +461,7 @@ func (app *App) ProcessExitChoicesA(promptstr string, response *string, message userid := message.UserID if config.GetGroupContext() && message.MessageType != "private" { - userid = message.GroupID + userid = message.GroupID + message.SelfID } // 获取用户剧情存档中的当前状态 @@ -538,7 +538,7 @@ func (app *App) ProcessExitChoicesA(promptstr string, response *string, message func (app *App) ApplySwitchOnA(promptstr *string, response *string, message *structs.OnebotGroupMessage) { userid := message.UserID if config.GetGroupContext() && message.MessageType != "private" { - userid = message.GroupID + userid = message.GroupID + message.SelfID } // 获取与 switchOnA 相关的选择 @@ -624,7 +624,7 @@ func (app *App) ApplySwitchOnA(promptstr *string, response *string, message *str func (app *App) ApplyPromptChoiceA(promptstr string, response string, message *structs.OnebotGroupMessage) string { userid := message.UserID if config.GetGroupContext() && message.MessageType != "private" { - userid = message.GroupID + userid = message.GroupID + message.SelfID } promptChoices := config.GetPromptChoicesA(promptstr) diff --git a/applogic/gensokyo.go b/applogic/gensokyo.go index 8cd2e2e..f7a93a2 100644 --- a/applogic/gensokyo.go +++ b/applogic/gensokyo.go @@ -176,7 +176,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { var CustomRecord *structs.CustomRecord if config.GetGroupContext() && message.MessageType != "private" { // 从数据库读取用户的剧情存档 - CustomRecord, err = app.FetchCustomRecord(message.GroupID) + CustomRecord, err = app.FetchCustomRecord(message.GroupID + message.SelfID) if err != nil { fmt.Printf("app.FetchCustomRecord 出错: %s\n", err) } @@ -197,7 +197,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { newPromptStrStat := CustomRecord.PromptStrStat - 1 // 根据条件区分群和私聊 if config.GetGroupContext() && message.MessageType != "private" { - err = app.InsertCustomTableRecord(message.GroupID, promptstr, newPromptStrStat) + err = app.InsertCustomTableRecord(message.GroupID+message.SelfID, promptstr, newPromptStrStat) if err != nil { fmt.Printf("app.InsertCustomTableRecord 出错: %s\n", err) } @@ -211,7 +211,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // MARK: 提示词之间 整体切换Q if config.GetGroupContext() && message.MessageType != "private" { - app.ProcessPromptMarks(message.GroupID, message.Message.(string), &promptstr) + app.ProcessPromptMarks(message.GroupID+message.SelfID, message.Message.(string), &promptstr) } else { app.ProcessPromptMarks(message.UserID, message.Message.(string), &promptstr) } @@ -234,7 +234,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // 刷新新的提示词给用户目前的状态 新的场景应该从1开始 if config.GetGroupContext() && message.MessageType != "private" { - app.InsertCustomTableRecord(message.GroupID, newPromptStr, 1) + app.InsertCustomTableRecord(message.GroupID+message.SelfID, newPromptStr, 1) } else { app.InsertCustomTableRecord(message.UserID, newPromptStr, 1) } @@ -246,7 +246,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } else { // MARK: 提示词之间 整体切换Q 当用户没有存档时 if config.GetGroupContext() && message.MessageType != "private" { - app.ProcessPromptMarks(message.GroupID, message.Message.(string), &promptstr) + app.ProcessPromptMarks(message.GroupID+message.SelfID, message.Message.(string), &promptstr) } else { app.ProcessPromptMarks(message.UserID, message.Message.(string), &promptstr) } @@ -260,7 +260,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // 初始状态就是 1 设置了1000以上长度的是固有场景,不可切换 if config.GetGroupContext() && message.MessageType != "private" { - err = app.InsertCustomTableRecord(message.GroupID, promptstr, newstat) + err = app.InsertCustomTableRecord(message.GroupID+message.SelfID, promptstr, newstat) } else { err = app.InsertCustomTableRecord(message.UserID, promptstr, newstat) } @@ -353,7 +353,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { if isResetCommand { fmtf.Println("处理重置操作") if config.GetGroupContext() && message.MessageType != "private" { - app.migrateUserToNewContext(message.GroupID) + app.migrateUserToNewContext(message.GroupID + message.SelfID) } else { app.migrateUserToNewContext(message.UserID) } @@ -369,7 +369,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } // 处理故事情节的重置 if config.GetGroupContext() && message.MessageType != "private" { - app.deleteCustomRecord(message.GroupID) + app.deleteCustomRecord(message.GroupID + message.SelfID) } else { app.deleteCustomRecord(message.UserID) } @@ -558,7 +558,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { var conversationID, parentMessageID string // 请求conversation api 增加当前群/用户上下文 if config.GetGroupContext() && message.MessageType != "private" { - conversationID, parentMessageID, err = app.handleUserContext(message.GroupID) + conversationID, parentMessageID, err = app.handleUserContext(message.GroupID + message.SelfID) } else { conversationID, parentMessageID, err = app.handleUserContext(message.UserID) } @@ -674,7 +674,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // 从数据库读取用户的剧情存档 var CustomRecord *structs.CustomRecord if config.GetGroupContext() && message.MessageType != "private" { - CustomRecord, err = app.FetchCustomRecord(message.GroupID) + CustomRecord, err = app.FetchCustomRecord(message.GroupID + message.SelfID) if err != nil { fmt.Printf("app.FetchCustomRecord 出错: %s\n", err) } @@ -706,7 +706,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } if config.GetGroupContext() && message.MessageType != "private" { - fmtf.Printf("实际请求conversation端点内容:[%v]%v\n", message.GroupID, requestmsg) + fmtf.Printf("实际请求conversation端点内容:[%v]%v\n", message.GroupID+message.SelfID, requestmsg) } else { fmtf.Printf("实际请求conversation端点内容:[%v]%v\n", message.UserID, requestmsg) } @@ -964,7 +964,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { if lastMessageID != "" { fmtf.Printf("lastMessageID: %s\n", lastMessageID) if config.GetGroupContext() && message.MessageType != "private" { - err := app.updateUserContext(message.GroupID, lastMessageID) + err := app.updateUserContext(message.GroupID+message.SelfID, lastMessageID) if err != nil { fmtf.Printf("Error updating user context: %v\n", err) } @@ -1069,7 +1069,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // 更新用户上下文 if messageId, ok := responseData["messageId"].(string); ok { if config.GetGroupContext() && message.MessageType != "private" { - err := app.updateUserContext(message.GroupID, messageId) + err := app.updateUserContext(message.GroupID+message.SelfID, messageId) if err != nil { fmtf.Printf("Error updating user context: %v\n", err) } From c22ebe8ed5fd3d041de67d1e025b8d5ebb04edf2 Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Sat, 8 Jun 2024 22:09:32 +0800 Subject: [PATCH 29/30] Beta142 (#141) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 * beta117 * beta118 * beta119 * beta120 * beta121 * beta122 * beta123 * beta124 * beta125 * beta126 * beta127 * beta128 * beta129 * beta130 * beta131 * beta132 * beta1333 * beta134 * beta135 * beta136 * beta137 * beta138 * beta139 * beta140 * beta141 * beta142 --- applogic/chatglm.go | 2 +- applogic/chatgpt.go | 2 +- applogic/ernie.go | 4 +- applogic/ernie_function.go | 4 +- applogic/flow.go | 16 ++-- applogic/gensokyo.go | 38 ++++---- applogic/hunyuan.go | 6 +- applogic/memory.go | 8 +- applogic/rwkv.go | 2 +- applogic/tongyiqianwen.go | 6 +- applogic/yuanqi.go | 9 +- config/config.go | 169 +++++++++++++++++++++++++++++++++--- structs/struct.go | 61 +++++++------ template/config_template.go | 9 +- utils/utils.go | 142 +++++++++++++++++++++++++++--- 15 files changed, 377 insertions(+), 101 deletions(-) diff --git a/applogic/chatglm.go b/applogic/chatglm.go index 45eb34a..5ee8315 100644 --- a/applogic/chatglm.go +++ b/applogic/chatglm.go @@ -271,7 +271,7 @@ func (app *App) ChatHandlerGlm(w http.ResponseWriter, r *http.Request) { } defer resp.Body.Close() - if !config.GetuseSse(promptstr) { + if config.GetuseSse(promptstr) < 2 { // 处理响应 responseBody, err := io.ReadAll(resp.Body) if err != nil { diff --git a/applogic/chatgpt.go b/applogic/chatgpt.go index 0816252..1722e37 100644 --- a/applogic/chatgpt.go +++ b/applogic/chatgpt.go @@ -278,7 +278,7 @@ func (app *App) ChatHandlerChatgpt(w http.ResponseWriter, r *http.Request) { } defer resp.Body.Close() - if !config.GetuseSse(promptstr) { + if config.GetuseSse(promptstr) < 2 { // 处理响应 responseBody, err := io.ReadAll(resp.Body) if err != nil { diff --git a/applogic/ernie.go b/applogic/ernie.go index eadb9d8..7355fd7 100644 --- a/applogic/ernie.go +++ b/applogic/ernie.go @@ -196,7 +196,7 @@ func (app *App) ChatHandlerErnie(w http.ResponseWriter, r *http.Request) { payload.MaxOutputTokens = MaxOutputTokens // 是否sse - if config.GetuseSse(promptstr) { + if config.GetuseSse(promptstr) == 2 { payload.Stream = true } @@ -258,7 +258,7 @@ func (app *App) ChatHandlerErnie(w http.ResponseWriter, r *http.Request) { rateLimitRequests, rateLimitTokens, remainingRequests, remainingTokens) // 检查是否不使用SSE - if !config.GetuseSse(promptstr) { + if config.GetuseSse(promptstr) < 2 { // 读取整个响应体到内存中 bodyBytes, err := io.ReadAll(resp.Body) if err != nil { diff --git a/applogic/ernie_function.go b/applogic/ernie_function.go index 6d3d967..29a078f 100644 --- a/applogic/ernie_function.go +++ b/applogic/ernie_function.go @@ -80,7 +80,7 @@ func (app *App) ChatHandlerErnieFunction(w http.ResponseWriter, r *http.Request) payload.ToolChoice.Function.Name = "predict_followup_questions" // 是否sse - if config.GetuseSse() { + if config.GetuseSse() == 2 { payload.Stream = true } @@ -130,7 +130,7 @@ func (app *App) ChatHandlerErnieFunction(w http.ResponseWriter, r *http.Request) rateLimitRequests, rateLimitTokens, remainingRequests, remainingTokens) // 检查是否不使用SSE - if !config.GetuseSse() { + if config.GetuseSse() < 2 { // 读取整个响应体到内存中 bodyBytes, err := io.ReadAll(resp.Body) if err != nil { diff --git a/applogic/flow.go b/applogic/flow.go index 4488065..a3a0daf 100644 --- a/applogic/flow.go +++ b/applogic/flow.go @@ -15,7 +15,7 @@ import ( // ApplyPromptChoiceQ 应用promptchoiceQ的逻辑,动态修改requestmsg func (app *App) ApplyPromptChoiceQ(promptstr string, requestmsg *string, message *structs.OnebotGroupMessage) { userid := message.UserID - if config.GetGroupContext() && message.MessageType != "private" { + if config.GetGroupContext() == 2 && message.MessageType != "private" { userid = message.GroupID + message.SelfID } @@ -138,7 +138,7 @@ func (app *App) ApplyPromptChoiceQ(promptstr string, requestmsg *string, message // ApplyPromptCoverQ 应用promptCoverQ的逻辑,动态覆盖requestmsg func (app *App) ApplyPromptCoverQ(promptstr string, requestmsg *string, message *structs.OnebotGroupMessage) { userid := message.UserID - if config.GetGroupContext() && message.MessageType != "private" { + if config.GetGroupContext() == 2 && message.MessageType != "private" { userid = message.GroupID + message.SelfID } @@ -250,7 +250,7 @@ func (app *App) ApplyPromptCoverQ(promptstr string, requestmsg *string, message // ApplySwitchOnQ 应用switchOnQ的逻辑,动态修改promptstr func (app *App) ApplySwitchOnQ(promptstr *string, requestmsg *string, message *structs.OnebotGroupMessage) { userid := message.UserID - if config.GetGroupContext() && message.MessageType != "private" { + if config.GetGroupContext() == 2 && message.MessageType != "private" { userid = message.GroupID + message.SelfID } @@ -356,7 +356,7 @@ func (app *App) ProcessExitChoicesQ(promptstr string, requestmsg *string, messag } userid := message.UserID - if config.GetGroupContext() && message.MessageType != "private" { + if config.GetGroupContext() == 2 && message.MessageType != "private" { userid = message.GroupID + message.SelfID } @@ -433,7 +433,7 @@ func (app *App) ProcessExitChoicesQ(promptstr string, requestmsg *string, messag // HandleExit 处理用户退出逻辑,包括发送消息和重置用户状态。 func (app *App) HandleExit(exitText string, message *structs.OnebotGroupMessage, selfid string, promptstr string) { userid := message.UserID - if config.GetGroupContext() && message.MessageType != "private" { + if config.GetGroupContext() == 2 && message.MessageType != "private" { userid = message.GroupID + message.SelfID } @@ -460,7 +460,7 @@ func (app *App) ProcessExitChoicesA(promptstr string, response *string, message } userid := message.UserID - if config.GetGroupContext() && message.MessageType != "private" { + if config.GetGroupContext() == 2 && message.MessageType != "private" { userid = message.GroupID + message.SelfID } @@ -537,7 +537,7 @@ func (app *App) ProcessExitChoicesA(promptstr string, response *string, message // ApplySwitchOnA 应用switchOnA的逻辑,动态修改promptstr func (app *App) ApplySwitchOnA(promptstr *string, response *string, message *structs.OnebotGroupMessage) { userid := message.UserID - if config.GetGroupContext() && message.MessageType != "private" { + if config.GetGroupContext() == 2 && message.MessageType != "private" { userid = message.GroupID + message.SelfID } @@ -623,7 +623,7 @@ func (app *App) ApplySwitchOnA(promptstr *string, response *string, message *str // ApplyPromptChoiceA 应用故事模式的情绪增强逻辑,并返回增强内容。 func (app *App) ApplyPromptChoiceA(promptstr string, response string, message *structs.OnebotGroupMessage) string { userid := message.UserID - if config.GetGroupContext() && message.MessageType != "private" { + if config.GetGroupContext() == 2 && message.MessageType != "private" { userid = message.GroupID + message.SelfID } diff --git a/applogic/gensokyo.go b/applogic/gensokyo.go index f7a93a2..f9c2a54 100644 --- a/applogic/gensokyo.go +++ b/applogic/gensokyo.go @@ -174,7 +174,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } var CustomRecord *structs.CustomRecord - if config.GetGroupContext() && message.MessageType != "private" { + if config.GetGroupContext() == 2 && message.MessageType != "private" { // 从数据库读取用户的剧情存档 CustomRecord, err = app.FetchCustomRecord(message.GroupID + message.SelfID) if err != nil { @@ -196,7 +196,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { fmt.Printf("刷新prompt参数: %s,newPromptStrStat:%d\n", promptstr, CustomRecord.PromptStrStat-1) newPromptStrStat := CustomRecord.PromptStrStat - 1 // 根据条件区分群和私聊 - if config.GetGroupContext() && message.MessageType != "private" { + if config.GetGroupContext() == 2 && message.MessageType != "private" { err = app.InsertCustomTableRecord(message.GroupID+message.SelfID, promptstr, newPromptStrStat) if err != nil { fmt.Printf("app.InsertCustomTableRecord 出错: %s\n", err) @@ -210,7 +210,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } // MARK: 提示词之间 整体切换Q - if config.GetGroupContext() && message.MessageType != "private" { + if config.GetGroupContext() == 2 && message.MessageType != "private" { app.ProcessPromptMarks(message.GroupID+message.SelfID, message.Message.(string), &promptstr) } else { app.ProcessPromptMarks(message.UserID, message.Message.(string), &promptstr) @@ -233,7 +233,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } // 刷新新的提示词给用户目前的状态 新的场景应该从1开始 - if config.GetGroupContext() && message.MessageType != "private" { + if config.GetGroupContext() == 2 && message.MessageType != "private" { app.InsertCustomTableRecord(message.GroupID+message.SelfID, newPromptStr, 1) } else { app.InsertCustomTableRecord(message.UserID, newPromptStr, 1) @@ -245,7 +245,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } } else { // MARK: 提示词之间 整体切换Q 当用户没有存档时 - if config.GetGroupContext() && message.MessageType != "private" { + if config.GetGroupContext() == 2 && message.MessageType != "private" { app.ProcessPromptMarks(message.GroupID+message.SelfID, message.Message.(string), &promptstr) } else { app.ProcessPromptMarks(message.UserID, message.Message.(string), &promptstr) @@ -259,7 +259,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } // 初始状态就是 1 设置了1000以上长度的是固有场景,不可切换 - if config.GetGroupContext() && message.MessageType != "private" { + if config.GetGroupContext() == 2 && message.MessageType != "private" { err = app.InsertCustomTableRecord(message.GroupID+message.SelfID, promptstr, newstat) } else { err = app.InsertCustomTableRecord(message.UserID, promptstr, newstat) @@ -333,7 +333,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } // 去除at自己的 CQ码,如果不是指向自己的,则不响应 - checkResetCommand = utils.RemoveAtTagContentConditional(checkResetCommand, message.SelfID) + checkResetCommand = utils.RemoveAtTagContentConditionalWithoutAddNick(checkResetCommand, message) // 检查checkResetCommand是否在restoreCommands列表中 isResetCommand := false @@ -352,7 +352,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { //处理重置指令 if isResetCommand { fmtf.Println("处理重置操作") - if config.GetGroupContext() && message.MessageType != "private" { + if config.GetGroupContext() == 2 && message.MessageType != "private" { app.migrateUserToNewContext(message.GroupID + message.SelfID) } else { app.migrateUserToNewContext(message.UserID) @@ -368,7 +368,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { utils.SendGroupMessage(message.GroupID, message.UserID, RestoreResponse, selfid, promptstr) } // 处理故事情节的重置 - if config.GetGroupContext() && message.MessageType != "private" { + if config.GetGroupContext() == 2 && message.MessageType != "private" { app.deleteCustomRecord(message.GroupID + message.SelfID) } else { app.deleteCustomRecord(message.UserID) @@ -428,7 +428,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } // 如果使用向量缓存 或者使用 向量安全词 - if config.GetUseCache(promptstr) || config.GetVectorSensitiveFilter() { + if config.GetUseCache(promptstr) == 2 || config.GetVectorSensitiveFilter() { if config.GetPrintHanming() { fmtf.Printf("计算向量的文本: %v", newmsg) } @@ -463,7 +463,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } // 缓存省钱部分 - if config.GetUseCache(promptstr) { + if config.GetUseCache(promptstr) == 2 { //fmtf.Printf("计算向量: %v", vector) cacheThreshold := config.GetCacheThreshold() // 搜索相似文本和对应的ID @@ -557,7 +557,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { var conversationID, parentMessageID string // 请求conversation api 增加当前群/用户上下文 - if config.GetGroupContext() && message.MessageType != "private" { + if config.GetGroupContext() == 2 && message.MessageType != "private" { conversationID, parentMessageID, err = app.handleUserContext(message.GroupID + message.SelfID) } else { conversationID, parentMessageID, err = app.handleUserContext(message.UserID) @@ -673,7 +673,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // 从数据库读取用户的剧情存档 var CustomRecord *structs.CustomRecord - if config.GetGroupContext() && message.MessageType != "private" { + if config.GetGroupContext() == 2 && message.MessageType != "private" { CustomRecord, err = app.FetchCustomRecord(message.GroupID + message.SelfID) if err != nil { fmt.Printf("app.FetchCustomRecord 出错: %s\n", err) @@ -696,7 +696,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { requestmsg = utils.ReplaceTextIn(requestmsg, promptstr) // 去除不是针对自己的at CQ码 不响应目标不是自己的at信息 - requestmsg = utils.RemoveAtTagContentConditional(requestmsg, message.SelfID) + requestmsg = utils.RemoveAtTagContentConditional(requestmsg, message, promptstr) if requestmsg == "" { fmtf.Printf("requestmsg is empty") // 发送响应 @@ -705,7 +705,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { return } - if config.GetGroupContext() && message.MessageType != "private" { + if config.GetGroupContext() == 2 && message.MessageType != "private" { fmtf.Printf("实际请求conversation端点内容:[%v]%v\n", message.GroupID+message.SelfID, requestmsg) } else { fmtf.Printf("实际请求conversation端点内容:[%v]%v\n", message.UserID, requestmsg) @@ -779,7 +779,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { var response string var EnhancedAContent string - if config.GetuseSse(promptstr) { + if config.GetuseSse(promptstr) == 2 { // 处理SSE流式响应 reader := bufio.NewReader(resp.Body) for { @@ -925,7 +925,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { app.ProcessPromptMarks(userinfo.UserID, response, &promptstr) // 清空之前加入缓存 // 缓存省钱部分 这里默认不被覆盖,如果主配置开了缓存,始终缓存. - if config.GetUseCache() { + if config.GetUseCache() == 2 { if response != "" { fmtf.Printf("缓存了Q:%v,A:%v,向量ID:%v", newmsg, response, lastSelectedVectorID) app.InsertQAEntry(newmsg, response, lastSelectedVectorID) @@ -963,7 +963,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // 在SSE流结束后更新用户上下文 在这里调用gensokyo流式接口的最后一步 插推荐气泡 if lastMessageID != "" { fmtf.Printf("lastMessageID: %s\n", lastMessageID) - if config.GetGroupContext() && message.MessageType != "private" { + if config.GetGroupContext() == 2 && message.MessageType != "private" { err := app.updateUserContext(message.GroupID+message.SelfID, lastMessageID) if err != nil { fmtf.Printf("Error updating user context: %v\n", err) @@ -1068,7 +1068,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { // 更新用户上下文 if messageId, ok := responseData["messageId"].(string); ok { - if config.GetGroupContext() && message.MessageType != "private" { + if config.GetGroupContext() == 2 && message.MessageType != "private" { err := app.updateUserContext(message.GroupID+message.SelfID, messageId) if err != nil { fmtf.Printf("Error updating user context: %v\n", err) diff --git a/applogic/hunyuan.go b/applogic/hunyuan.go index 913eadc..192e03e 100644 --- a/applogic/hunyuan.go +++ b/applogic/hunyuan.go @@ -244,7 +244,7 @@ func (app *App) ChatHandlerHunyuan(w http.ResponseWriter, r *http.Request) { http.Error(w, fmtf.Sprintf("hunyuanapi返回错误: %v", err), http.StatusInternalServerError) return } - if !config.GetuseSse(promptstr) { + if config.GetuseSse(promptstr) < 2 { // 解析响应 var responseTextBuilder strings.Builder var totalUsage structs.UsageInfo @@ -412,7 +412,7 @@ func (app *App) ChatHandlerHunyuan(w http.ResponseWriter, r *http.Request) { http.Error(w, fmtf.Sprintf("hunyuanapi返回错误: %v", err), http.StatusInternalServerError) return } - if !config.GetuseSse(promptstr) { + if config.GetuseSse(promptstr) < 2 { // 解析响应 var responseTextBuilder strings.Builder var totalUsage structs.UsageInfo @@ -598,7 +598,7 @@ func (app *App) ChatHandlerHunyuan(w http.ResponseWriter, r *http.Request) { http.Error(w, fmtf.Sprintf("hunyuanapi返回错误: %v", err), http.StatusInternalServerError) return } - if !config.GetuseSse(promptstr) { + if config.GetuseSse(promptstr) < 2 { // 解析响应 var responseTextBuilder strings.Builder var totalUsage structs.UsageInfo diff --git a/applogic/memory.go b/applogic/memory.go index 832f965..4c6fd69 100644 --- a/applogic/memory.go +++ b/applogic/memory.go @@ -85,7 +85,7 @@ func (app *App) handleSaveMemory(msg structs.OnebotGroupMessage, ConversationID conversationTitle := "2024-5-19/18:26" // 默认标题,根据实际需求可能需要调整为动态生成的时间戳 userid := msg.UserID - if config.GetGroupContext() && msg.MessageType != "private" { + if config.GetGroupContext() == 2 && msg.MessageType != "private" { userid = msg.GroupID } @@ -131,7 +131,7 @@ func (app *App) handleSaveMemory(msg structs.OnebotGroupMessage, ConversationID func (app *App) handleMemoryList(msg structs.OnebotGroupMessage, promptstr string) { userid := msg.UserID - if config.GetGroupContext() && msg.MessageType != "private" { + if config.GetGroupContext() == 2 && msg.MessageType != "private" { userid = msg.GroupID } @@ -179,7 +179,7 @@ func (app *App) handleMemoryList(msg structs.OnebotGroupMessage, promptstr strin func (app *App) handleLoadMemory(msg structs.OnebotGroupMessage, checkResetCommand string, promptstr string) { userid := msg.UserID - if config.GetGroupContext() && msg.MessageType != "private" { + if config.GetGroupContext() == 2 && msg.MessageType != "private" { userid = msg.GroupID } @@ -285,7 +285,7 @@ func (app *App) handleNewConversation(msg structs.OnebotGroupMessage, conversati conversationTitle := "2024-5-19/18:26" // 实际应用中应使用动态生成的时间戳 userid := msg.UserID - if config.GetGroupContext() && msg.MessageType != "private" { + if config.GetGroupContext() == 2 && msg.MessageType != "private" { userid = msg.GroupID } diff --git a/applogic/rwkv.go b/applogic/rwkv.go index ad205a2..ab5f9c8 100644 --- a/applogic/rwkv.go +++ b/applogic/rwkv.go @@ -263,7 +263,7 @@ func (app *App) ChatHandlerRwkv(w http.ResponseWriter, r *http.Request) { } defer resp.Body.Close() - if !config.GetuseSse(promptstr) { + if config.GetuseSse(promptstr) < 2 { // 处理响应 responseBody, err := io.ReadAll(resp.Body) if err != nil { diff --git a/applogic/tongyiqianwen.go b/applogic/tongyiqianwen.go index b4bb6a1..e20feb2 100644 --- a/applogic/tongyiqianwen.go +++ b/applogic/tongyiqianwen.go @@ -232,7 +232,7 @@ func (app *App) ChatHandlerTyqw(w http.ResponseWriter, r *http.Request) { useSSE := config.GetuseSse(promptstr) apiKey := config.GetTyqwKey(promptstr) var requestBody map[string]interface{} - if useSSE { + if useSSE == 2 { // 构建请求体,根据提供的文档重新调整 requestBody = map[string]interface{}{ "parameters": map[string]interface{}{ @@ -299,7 +299,7 @@ func (app *App) ChatHandlerTyqw(w http.ResponseWriter, r *http.Request) { req.Header.Set("Authorization", "Bearer "+apiKey) // 根据是否使用SSE来设置Accept和X-DashScope-SSE - if useSSE { + if useSSE == 2 { req.Header.Set("Accept", "text/event-stream") req.Header.Set("X-DashScope-SSE", "enable") } @@ -319,7 +319,7 @@ func (app *App) ChatHandlerTyqw(w http.ResponseWriter, r *http.Request) { } defer resp.Body.Close() - if !config.GetuseSse(promptstr) { + if config.GetuseSse(promptstr) < 2 { // 处理响应 responseBody, err := io.ReadAll(resp.Body) if err != nil { diff --git a/applogic/yuanqi.go b/applogic/yuanqi.go index b88a061..4dd21a4 100644 --- a/applogic/yuanqi.go +++ b/applogic/yuanqi.go @@ -226,11 +226,16 @@ func (app *App) ChatHandlerYuanQi(w http.ResponseWriter, r *http.Request) { // 保持QA顺序 即使用户发送多张图片 messages = utils.MakeAlternating(messages) + usesse := false + if config.GetuseSse(promptstr) == 2 { + usesse = true + } + // 创建请求数据结构体 requestBody = structs.RequestDataYuanQi{ AssistantID: assistantID, UserID: useridstr, - Stream: config.GetuseSse(promptstr), + Stream: usesse, ChatType: config.GetYuanqiChatType(promptstr), Messages: messages, } @@ -281,7 +286,7 @@ func (app *App) ChatHandlerYuanQi(w http.ResponseWriter, r *http.Request) { } defer resp.Body.Close() - if !config.GetuseSse(promptstr) { + if config.GetuseSse(promptstr) < 2 { // 处理响应 responseBody, err := io.ReadAll(resp.Body) if err != nil { diff --git a/config/config.go b/config/config.go index acc23bd..c2bdd38 100644 --- a/config/config.go +++ b/config/config.go @@ -104,20 +104,20 @@ func Getregion() string { } // 获取useSse -func GetuseSse(options ...string) bool { +func GetuseSse(options ...string) int { mu.Lock() defer mu.Unlock() return getUseSseInternal(options...) } // 内部逻辑执行函数,不处理锁,可以安全地递归调用 -func getUseSseInternal(options ...string) bool { +func getUseSseInternal(options ...string) int { // 检查是否有参数传递进来,以及是否为空字符串 if len(options) == 0 || options[0] == "" { if instance != nil { return instance.Settings.UseSse } - return false + return 0 } // 使用传入的 basename @@ -128,8 +128,8 @@ func getUseSseInternal(options ...string) bool { return getUseSseInternal() // 递归调用内部函数,不传递任何参数 } - useSse, ok := useSseInterface.(bool) - if !ok { // 检查是否断言失败 + useSse, ok := useSseInterface.(int) + if !ok || useSse == 0 { // 检查是否断言失败 或者是0 log.Println("Type assertion failed for UseSse, fetching default") return getUseSseInternal() // 递归调用内部函数,不传递任何参数 } @@ -945,20 +945,20 @@ func GetAntiPromptLimit() float64 { } // 获取UseCache,增加可选参数支持动态配置查询 -func GetUseCache(options ...string) bool { +func GetUseCache(options ...string) int { mu.Lock() defer mu.Unlock() return getUseCacheInternal(options...) } // 内部逻辑执行函数,不处理锁,可以安全地递归调用 -func getUseCacheInternal(options ...string) bool { +func getUseCacheInternal(options ...string) int { // 检查是否有参数传递进来,以及是否为空字符串 if len(options) == 0 || options[0] == "" { if instance != nil { return instance.Settings.UseCache } - return false + return 0 } // 使用传入的 basename @@ -969,8 +969,8 @@ func getUseCacheInternal(options ...string) bool { return getUseCacheInternal() // 如果出错,递归调用自身,不传递任何参数 } - useCache, ok := useCacheInterface.(bool) - if !ok { + useCache, ok := useCacheInterface.(int) + if !ok || useCache == 0 { log.Println("Type assertion failed for UseCache") return getUseCacheInternal() // 如果类型断言失败,递归调用自身,不传递任何参数 } @@ -1217,13 +1217,37 @@ func GetNoContext() bool { } // 获取GroupContext -func GetGroupContext() bool { +func GetGroupContext(options ...string) int { mu.Lock() defer mu.Unlock() - if instance != nil { - return instance.Settings.GroupContext + return getGroupContextInternal(options...) +} + +// 内部逻辑执行函数,不处理锁,可以安全地递归调用 +func getGroupContextInternal(options ...string) int { + // 检查是否有参数传递进来,以及是否为空字符串 + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.GroupContext + } + return 0 } - return false + + // 使用传入的 basename + basename := options[0] + GroupContextInterface, err := prompt.GetSettingFromFilename(basename, "GroupContext") + if err != nil { + log.Println("Error retrieving GroupContext:", err) + return getGroupContextInternal() // 递归调用内部函数,不传递任何参数 + } + + GroupContext, ok := GroupContextInterface.(int) + if !ok || GroupContext == 0 { // 检查是否断言失败或结果为空字符串 + log.Println("Type assertion failed or empty string for GroupContext, fetching default") + return getGroupContextInternal() // 递归调用内部函数,不传递任何参数 + } + + return GroupContext } // 获取WithdrawCommand @@ -3286,3 +3310,120 @@ func getYuanqiMaxTokenInternal(options ...string) int { return YuanqiMaxToken } + +// 获取GroupAddNicknameToQ +func GetGroupAddNicknameToQ(options ...string) int { + mu.Lock() + defer mu.Unlock() + return getGroupAddNicknameToQInternal(options...) +} + +// 内部逻辑执行函数,不处理锁,可以安全地递归调用 +func getGroupAddNicknameToQInternal(options ...string) int { + // 检查是否有参数传递进来,以及是否为空字符串 + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.GroupAddNicknameToQ + } + return 0 + } + + // 使用传入的 basename + basename := options[0] + YuanqiMaxTokenInterface, err := prompt.GetSettingFromFilename(basename, "GroupAddNicknameToQ") + if err != nil { + log.Println("Error retrieving GroupAddNicknameToQ:", err) + return getGroupAddNicknameToQInternal() // 递归调用内部函数,不传递任何参数 + } + + GroupAddNicknameToQ, ok := YuanqiMaxTokenInterface.(int) + if !ok { // 检查是否断言失败 + fmt.Println("Type assertion failed for GroupAddNicknameToQ, fetching default") + return getGroupAddNicknameToQInternal() // 递归调用内部函数,不传递任何参数 + } + + if GroupAddNicknameToQ == 0 { + return getGroupAddNicknameToQInternal() // 递归调用内部函数,不传递任何参数 + } + + return GroupAddNicknameToQ +} + +// 获取GroupAddCardToQ +func GetGroupAddCardToQ(options ...string) int { + mu.Lock() + defer mu.Unlock() + return getGroupAddCardToQInternal(options...) +} + +// 内部逻辑执行函数,不处理锁,可以安全地递归调用 +func getGroupAddCardToQInternal(options ...string) int { + // 检查是否有参数传递进来,以及是否为空字符串 + if len(options) == 0 || options[0] == "" { + if instance != nil { + return instance.Settings.GroupAddCardToQ + } + return 0 + } + + // 使用传入的 basename + basename := options[0] + GroupAddCardToQInterface, err := prompt.GetSettingFromFilename(basename, "GroupAddCardToQ") + if err != nil { + log.Println("Error retrieving GroupAddCardToQ:", err) + return getGroupAddCardToQInternal() // 递归调用内部函数,不传递任何参数 + } + + GroupAddCardToQ, ok := GroupAddCardToQInterface.(int) + if !ok { // 检查是否断言失败 + fmt.Println("Type assertion failed for GroupAddCardToQ, fetching default") + return getGroupAddCardToQInternal() // 递归调用内部函数,不传递任何参数 + } + + if GroupAddCardToQ == 0 { + return getGroupAddCardToQInternal() // 递归调用内部函数,不传递任何参数 + } + + return GroupAddCardToQ +} + +// 获取 SpecialNameToQ +func GetSpecialNameToQ(options ...string) []structs.ReplacementNamePair { + mu.Lock() + defer mu.Unlock() + return getSpecialNameToQInternal(options...) +} + +// getReplacementPairsInOutternal 内部递归函数,处理配置获取逻辑 +func getSpecialNameToQInternal(options ...string) []structs.ReplacementNamePair { + // 从instance中读取配置数组 + if len(options) == 0 || options[0] == "" { + if instance != nil && len(instance.Settings.SpecialNameToQ) > 0 { + return instance.Settings.SpecialNameToQ + } else { + return nil + } + } + + // 使用prompt包从指定的文件名中获取配置 + basename := options[0] + confInterface, err := prompt.GetSettingFromFilename(basename, "SpecialNameToQ") + if err != nil { + log.Printf("Error retrieving settings from file: %s, error: %v", basename, err) + return getSpecialNameToQInternal() // 递归调用内部函数,不传递任何参数 + } + + // 断言获取的interface{}为[]ReplacementPair + confs, ok := confInterface.([]structs.ReplacementNamePair) + if !ok { + log.Println("Type assertion failed for ReplacementPair, attempting default behavior") + return getSpecialNameToQInternal() // 递归调用内部函数,尝试默认配置 + } + + if len(confs) == 0 { + log.Println("No configurations found in file:", basename) + return getSpecialNameToQInternal() // 递归调用内部函数,尝试默认配置 + } + + return confs +} diff --git a/structs/struct.go b/structs/struct.go index 4b3514c..927bc1d 100644 --- a/structs/struct.go +++ b/structs/struct.go @@ -251,29 +251,38 @@ type ReplacementPair struct { TargetWord string `yaml:"targetWord"` } +// ReplacementNamePair 表示一个名字,其中包含id和目标名字。 +type ReplacementNamePair struct { + ID string `yaml:"id"` + Name string `yaml:"name"` +} + type Settings struct { - AllApi bool `yaml:"allApi"` - SecretId string `yaml:"secretId"` - SecretKey string `yaml:"secretKey"` - Region string `yaml:"region"` - UseSse bool `yaml:"useSse"` - Port int `yaml:"port"` - SelfPath string `yaml:"selfPath"` - HttpPath string `yaml:"path"` - HttpPaths []string `yaml:"paths"` - Lotus string `yaml:"lotus"` - PathToken string `yaml:"pathToken"` - SystemPrompt []string `yaml:"systemPrompt"` - IPWhiteList []string `yaml:"iPWhiteList"` - ApiType int `yaml:"apiType"` - Proxy string `yaml:"proxy"` - UrlSendPics bool `yaml:"urlSendPics"` // 自己构造图床加速图片发送 - MdPromptKeyboardAtGroup bool `yaml:"mdPromptKeyboardAtGroup"` // 群内使用md能力模拟PromptKeyboard - GroupHintWords []string `yaml:"groupHintWords"` - GroupHintChance int `yaml:"groupHintChance"` - GroupContext bool `yaml:"groupContext"` - ReplacementPairsIn []ReplacementPair `yaml:"replacementPairsIn"` - ReplacementPairsOut []ReplacementPair `yaml:"replacementPairsOut"` + AllApi bool `yaml:"allApi"` + SecretId string `yaml:"secretId"` + SecretKey string `yaml:"secretKey"` + Region string `yaml:"region"` + UseSse int `yaml:"useSse"` // 0 false 1 false 2 true + Port int `yaml:"port"` + SelfPath string `yaml:"selfPath"` + HttpPath string `yaml:"path"` + HttpPaths []string `yaml:"paths"` + Lotus string `yaml:"lotus"` + PathToken string `yaml:"pathToken"` + SystemPrompt []string `yaml:"systemPrompt"` + IPWhiteList []string `yaml:"iPWhiteList"` + ApiType int `yaml:"apiType"` + Proxy string `yaml:"proxy"` + UrlSendPics bool `yaml:"urlSendPics"` // 自己构造图床加速图片发送 + MdPromptKeyboardAtGroup bool `yaml:"mdPromptKeyboardAtGroup"` // 群内使用md能力模拟PromptKeyboard + GroupHintWords []string `yaml:"groupHintWords"` + GroupHintChance int `yaml:"groupHintChance"` + GroupContext int `yaml:"groupContext"` // 0 false 1 false 2 true + ReplacementPairsIn []ReplacementPair `yaml:"replacementPairsIn"` + ReplacementPairsOut []ReplacementPair `yaml:"replacementPairsOut"` + GroupAddNicknameToQ int `yaml:"groupAddNicknameToQ"` + GroupAddCardToQ int `yaml:"groupAddCardToQ"` + SpecialNameToQ []ReplacementNamePair `yaml:"specialNameToQ"` HunyuanType int `yaml:"hunyuanType"` MaxTokensHunyuan int `yaml:"maxTokensHunyuan"` @@ -323,10 +332,10 @@ type Settings struct { Savelogs bool `yaml:"savelogs"` AntiPromptLimit float64 `yaml:"antiPromptLimit"` - UseCache bool `yaml:"useCache"` - CacheThreshold int `yaml:"cacheThreshold"` - CacheChance int `yaml:"cacheChance"` - EmbeddingType int `yaml:"embeddingType"` + UseCache int `yaml:"useCache"` + CacheThreshold int `yaml:"cacheThreshold"` + CacheChance int `yaml:"cacheChance"` + EmbeddingType int `yaml:"embeddingType"` PrintHanming bool `yaml:"printHanming"` CacheK float64 `yaml:"cacheK"` diff --git a/template/config_template.go b/template/config_template.go index 2a1d2d2..40176c6 100644 --- a/template/config_template.go +++ b/template/config_template.go @@ -50,7 +50,12 @@ settings: urlSendPics : false #自己构造图床加速图片发送.需配置公网ip+放通port+设置正确的selfPath groupHintWords : [] #当机器人位于群内时,需满足包含groupHintWords数组任意内容如[CQ:at,qq=2] 机器人的名字 等 groupHintChance : 0 #需与groupHintWords联用,代表不满足hintwords时概率触发,不启用groupHintWords相当于百分百概率回复. - groupContext : false #群上下文 在智能体在群内时,以群为单位处理上下文. + groupContext : 0 #群上下文 在智能体在群内时,以群为单位处理上下文. 0=默认 1=一个人一个上下文 2=群聊共享上下文 + groupAddNicknameToQ : 0 #群上下文增加message.sender.nickname到上下文(昵称)让模型能知道发送者名字 0=默认 1=false 2=true + groupAddCardToQ : 0 #群上下文增加message.sender.card到上下文(群名片)让模型能知道发送者名字 0=默认 1=false 2=true + specialNameToQ: #开启groupAddNicknameToQ和groupAddCardToQ时有效,应用特殊规则,让模型对某个id产生特殊称谓 + - id: 12345 + name: "" replacementPairsIn: #每个不同的yml文件,都可以有自己独立的替换词规则,IN OUT分离 - originalWord: "hello" targetWord: "hi" @@ -83,7 +88,7 @@ settings: #不同种类的向量,维度和模型不同,所以请一开始决定好使用的向量,或者自行将数据库备份\对应,不同种类向量没有互相检索的能力。 embeddingType : 0 #0=混元向量 1=文心向量,需设置wenxinEmbeddingUrl 2=chatgpt向量,需设置gptEmbeddingUrl - useCache : false #使用缓存省钱. + useCache : 1 #使用缓存省钱. cacheThreshold : 100 #阈值,以汉明距离单位. hunyuan建议250-300 文心v1建议80-100,越小越精确. cacheChance : 100 #使用缓存的概率,前期10,积攒缓存,后期酌情增加,测试时100 printHanming : true #输出汉明距离,还有分片基数(norm*CacheK)等完全确认下来汉明距离、分片数后,再关闭这个选项。 diff --git a/utils/utils.go b/utils/utils.go index b02ad95..210bd20 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -887,14 +887,44 @@ func RemoveBracketsContent(input string) string { return re.ReplaceAllString(input, "") } -// RemoveAtTagContent 接收一个字符串,并移除所有[@任意字符]的内容 -func RemoveAtTagContent(input string) string { - // 编译一个正则表达式,用于匹配[@任意字符]的模式 - re := regexp.MustCompile(`\[@.*?\]`) - // 使用正则表达式的ReplaceAllString方法删除匹配的部分 - cleaned := re.ReplaceAllString(input, "") +// RemoveAtTagContentConditional 接收一个字符串和一个 int64 类型的 selfid, +// 并根据条件移除[@selfowd]格式的内容,然后去除前后的空格。 +// 只有当标签中的ID与传入的selfid相匹配时才进行移除, +// 如果没有任何[@xxx]出现或者所有出现的[@xxx]中的xxx都不等于selfid时,返回原字符串。 +func RemoveAtTagContentConditionalWithoutAddNick(input string, message structs.OnebotGroupMessage) string { + // 将 int64 类型的 selfid 转换为字符串 + selfidStr := strconv.FormatInt(message.SelfID, 10) + + // 编译一个正则表达式,用于匹配并捕获[@任意字符]的模式 + re := regexp.MustCompile(`\[@(.*?)\]`) + matches := re.FindAllStringSubmatch(input, -1) + + // 如果没有找到任何匹配项,直接返回原输入,代表不带at的信息,会在更上方判断是否处理.同时根据配置项为请求增加名字. + if len(matches) == 0 { //私聊无法at 只会走这里,只会有nick生效 + return input + } + + foundSelfId := false // 用于跟踪是否找到与selfid相匹配的标签 + + // 遍历所有匹配项 + for _, match := range matches { + if match[1] == selfidStr { + // 如果找到与selfid相匹配的标签,替换该标签 + input = strings.Replace(input, match[0], "", -1) + foundSelfId = true // 标记已找到至少一个与selfid相匹配的标签 + } else { + input = strings.Replace(input, match[0], "", -1) + } + } + + // 只有在包含了at 但是at不包含自己,才忽略信息 + if !foundSelfId { + // 如果没有找到任何与selfid相匹配的标签,将输入置为空,代表不响应这一条信息 + input = "" + } + // 去除前后的空格 - cleaned = strings.TrimSpace(cleaned) + cleaned := strings.TrimSpace(input) return cleaned } @@ -902,16 +932,48 @@ func RemoveAtTagContent(input string) string { // 并根据条件移除[@selfowd]格式的内容,然后去除前后的空格。 // 只有当标签中的ID与传入的selfid相匹配时才进行移除, // 如果没有任何[@xxx]出现或者所有出现的[@xxx]中的xxx都不等于selfid时,返回原字符串。 -func RemoveAtTagContentConditional(input string, selfid int64) string { +func RemoveAtTagContentConditional(input string, message structs.OnebotGroupMessage, promptstr string) string { + + // 获取特殊名称替换对数组 + specialNames := config.GetSpecialNameToQ(promptstr) + // 将 int64 类型的 selfid 转换为字符串 - selfidStr := strconv.FormatInt(selfid, 10) + selfidStr := strconv.FormatInt(message.SelfID, 10) // 编译一个正则表达式,用于匹配并捕获[@任意字符]的模式 re := regexp.MustCompile(`\[@(.*?)\]`) matches := re.FindAllStringSubmatch(input, -1) - // 如果没有找到任何匹配项,直接返回原输入 - if len(matches) == 0 { + // 如果没有找到任何匹配项,直接返回原输入,代表不带at的信息,会在更上方判断是否处理.同时根据配置项为请求增加名字. + if len(matches) == 0 { //私聊无法at 只会走这里,只会有nick生效 + var name string + name = "" + // 可以都是2 打开 呈现覆盖关系 + if config.GetGroupAddCardToQ(promptstr) == 2 { + if message.Sender.Card != "" { + name = "[name:" + message.Sender.Card + "]" + } + } + + if config.GetGroupAddNicknameToQ(promptstr) == 2 && name == "" { + if message.Sender.Nickname != "" { + name = "[name:" + message.Sender.Nickname + "]" + } + } + + useridstr := strconv.FormatInt(message.UserID, 10) + // 遍历特殊名称数组,检查是否需要进行进一步替换 + for _, replacement := range specialNames { + if useridstr == replacement.ID { + name = "[name:" + replacement.Name + "]" + break // 找到匹配,跳出循环 + } + } + + if name != "" { + input = name + input + } + return input } @@ -920,14 +982,68 @@ func RemoveAtTagContentConditional(input string, selfid int64) string { // 遍历所有匹配项 for _, match := range matches { if match[1] == selfidStr { + // 把at自己的信息替换为当前at自己的人的昵称或者群名片 + var name string + name = "" // 初始状态 + + // 可以都是2 打开 呈现覆盖关系 + if config.GetGroupAddCardToQ(promptstr) == 2 { + if message.Sender.Card != "" { + name = "[name:" + message.Sender.Card + "]" + } + } + + if config.GetGroupAddNicknameToQ(promptstr) == 2 && name == "" { + if message.Sender.Nickname != "" { + name = "[name:" + message.Sender.Nickname + "]" + } + } + + useridstr := strconv.FormatInt(message.UserID, 10) + // 遍历特殊名称数组,检查是否需要进行进一步替换 + for _, replacement := range specialNames { + if useridstr == replacement.ID { + name = "[name:" + replacement.Name + "]" + break // 找到匹配,跳出循环 + } + } + // 如果找到与selfid相匹配的标签,替换该标签 - input = strings.Replace(input, match[0], "", -1) + input = strings.Replace(input, match[0], name, -1) foundSelfId = true // 标记已找到至少一个与selfid相匹配的标签 + } else { + var name string + name = "" + + // 可以都是2 打开 呈现覆盖关系 + if config.GetGroupAddCardToQ(promptstr) == 2 { + if message.Sender.Card != "" { + name = "[name:" + message.Sender.Card + "]" + } + } + + // 将CQat标签替换为名字 + if config.GetGroupAddNicknameToQ(promptstr) == 2 && name == "" { + if message.Sender.Nickname != "" { + name = "[name:" + message.Sender.Nickname + "]" + } + } + + // 遍历特殊名称数组,检查是否需要进行进一步替换 + for _, replacement := range specialNames { + if match[1] == replacement.ID { + name = "[name:" + replacement.Name + "]" + break // 找到匹配,跳出循环 + } + } + + input = strings.Replace(input, match[0], name, -1) } } + // 只有在包含了at 但是at不包含自己,才忽略信息 if !foundSelfId { - // 如果没有找到任何与selfid相匹配的标签,将输入置为空 + // 如果没有找到任何与selfid相匹配的标签,将输入置为空,代表不响应这一条信息 input = "" } From 5a209ae0f01f77820ef542bc781175a300b26105 Mon Sep 17 00:00:00 2001 From: SanaeFox <36219542+Hoshinonyaruko@users.noreply.github.com> Date: Sat, 8 Jun 2024 22:17:49 +0800 Subject: [PATCH 30/30] Beta143 (#142) * beta1 * beta2 * beta3 * beta4 * beta5 * beta6 * beta7 * beta8 * beta9 * beta10 * beta11 * beta12 * beta13 * beta14 * beta15 * beta16 * beta16 * beta19 * beta20 * beta21 * beta22 * beta23 * beta24 * beta25 * beta27 * beta28 * beta29 * beta30 * beta31 * beta33 * beta34 * beta35 * beta36 * beta37 * beta38 * beta39 * beta40 * beta41 * beta42 * beta43 * beta44 * beta45 * beta45 * beta46 * beat48 * beta49 * beta50 * beta51 * beta52 * beta53 * beta54 * beta55 * beta57 * beta58 * beta59 * beta61 * beta62 * beta63 * beta63 * beta64 * beta65 * beta66 * beta67 * beta70 * beta71 * beta72 * beta72 * beta74 * beta75 * beta76 * beta77 * beta78 * beta79 * beta80 * beta81 * beta82 * beta83 * beta85 * beta86 * beta87 * beta88 * beta89 * beta90 * beta91 * beta92 * beta93 * beta94 * beta94 * beta96 * beta97 * beta98 * beta99 * beta100 * beta101 * beta102 * beta104 * beta105 * beta106 * beta107 * beta108 * beta109 * beta110 * beta111 * beta112 * beta113 * beta115 * beta116 * beta117 * beta118 * beta119 * beta120 * beta121 * beta122 * beta123 * beta124 * beta125 * beta126 * beta127 * beta128 * beta129 * beta130 * beta131 * beta132 * beta1333 * beta134 * beta135 * beta136 * beta137 * beta138 * beta139 * beta140 * beta141 * beta142 * beta143 --- applogic/flow.go | 16 ++++++++-------- applogic/gensokyo.go | 10 +++++----- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/applogic/flow.go b/applogic/flow.go index a3a0daf..fd06697 100644 --- a/applogic/flow.go +++ b/applogic/flow.go @@ -14,7 +14,7 @@ import ( // ApplyPromptChoiceQ 应用promptchoiceQ的逻辑,动态修改requestmsg func (app *App) ApplyPromptChoiceQ(promptstr string, requestmsg *string, message *structs.OnebotGroupMessage) { - userid := message.UserID + userid := message.UserID + message.SelfID if config.GetGroupContext() == 2 && message.MessageType != "private" { userid = message.GroupID + message.SelfID } @@ -137,7 +137,7 @@ func (app *App) ApplyPromptChoiceQ(promptstr string, requestmsg *string, message // ApplyPromptCoverQ 应用promptCoverQ的逻辑,动态覆盖requestmsg func (app *App) ApplyPromptCoverQ(promptstr string, requestmsg *string, message *structs.OnebotGroupMessage) { - userid := message.UserID + userid := message.UserID + message.SelfID if config.GetGroupContext() == 2 && message.MessageType != "private" { userid = message.GroupID + message.SelfID } @@ -249,7 +249,7 @@ func (app *App) ApplyPromptCoverQ(promptstr string, requestmsg *string, message // ApplySwitchOnQ 应用switchOnQ的逻辑,动态修改promptstr func (app *App) ApplySwitchOnQ(promptstr *string, requestmsg *string, message *structs.OnebotGroupMessage) { - userid := message.UserID + userid := message.UserID + message.SelfID if config.GetGroupContext() == 2 && message.MessageType != "private" { userid = message.GroupID + message.SelfID } @@ -355,7 +355,7 @@ func (app *App) ProcessExitChoicesQ(promptstr string, requestmsg *string, messag return } - userid := message.UserID + userid := message.UserID + message.SelfID if config.GetGroupContext() == 2 && message.MessageType != "private" { userid = message.GroupID + message.SelfID } @@ -432,7 +432,7 @@ func (app *App) ProcessExitChoicesQ(promptstr string, requestmsg *string, messag // HandleExit 处理用户退出逻辑,包括发送消息和重置用户状态。 func (app *App) HandleExit(exitText string, message *structs.OnebotGroupMessage, selfid string, promptstr string) { - userid := message.UserID + userid := message.UserID + message.SelfID if config.GetGroupContext() == 2 && message.MessageType != "private" { userid = message.GroupID + message.SelfID } @@ -459,7 +459,7 @@ func (app *App) ProcessExitChoicesA(promptstr string, response *string, message return } - userid := message.UserID + userid := message.UserID + message.SelfID if config.GetGroupContext() == 2 && message.MessageType != "private" { userid = message.GroupID + message.SelfID } @@ -536,7 +536,7 @@ func (app *App) ProcessExitChoicesA(promptstr string, response *string, message // ApplySwitchOnA 应用switchOnA的逻辑,动态修改promptstr func (app *App) ApplySwitchOnA(promptstr *string, response *string, message *structs.OnebotGroupMessage) { - userid := message.UserID + userid := message.UserID + message.SelfID if config.GetGroupContext() == 2 && message.MessageType != "private" { userid = message.GroupID + message.SelfID } @@ -622,7 +622,7 @@ func (app *App) ApplySwitchOnA(promptstr *string, response *string, message *str // ApplyPromptChoiceA 应用故事模式的情绪增强逻辑,并返回增强内容。 func (app *App) ApplyPromptChoiceA(promptstr string, response string, message *structs.OnebotGroupMessage) string { - userid := message.UserID + userid := message.UserID + message.SelfID if config.GetGroupContext() == 2 && message.MessageType != "private" { userid = message.GroupID + message.SelfID } diff --git a/applogic/gensokyo.go b/applogic/gensokyo.go index f9c2a54..0557d89 100644 --- a/applogic/gensokyo.go +++ b/applogic/gensokyo.go @@ -182,7 +182,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { } } else { // 从数据库读取用户的剧情存档 - CustomRecord, err = app.FetchCustomRecord(message.UserID) + CustomRecord, err = app.FetchCustomRecord(message.UserID + message.SelfID) if err != nil { fmt.Printf("app.FetchCustomRecord 出错: %s\n", err) } @@ -355,7 +355,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { if config.GetGroupContext() == 2 && message.MessageType != "private" { app.migrateUserToNewContext(message.GroupID + message.SelfID) } else { - app.migrateUserToNewContext(message.UserID) + app.migrateUserToNewContext(message.UserID + message.SelfID) } RestoreResponse := config.GetRandomRestoreResponses() if message.RealMessageType == "group_private" || message.MessageType == "private" { @@ -371,7 +371,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { if config.GetGroupContext() == 2 && message.MessageType != "private" { app.deleteCustomRecord(message.GroupID + message.SelfID) } else { - app.deleteCustomRecord(message.UserID) + app.deleteCustomRecord(message.UserID + message.SelfID) } return } @@ -560,7 +560,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { if config.GetGroupContext() == 2 && message.MessageType != "private" { conversationID, parentMessageID, err = app.handleUserContext(message.GroupID + message.SelfID) } else { - conversationID, parentMessageID, err = app.handleUserContext(message.UserID) + conversationID, parentMessageID, err = app.handleUserContext(message.UserID + message.SelfID) } // 使用map映射conversationID和uid gid的关系 @@ -679,7 +679,7 @@ func (app *App) GensokyoHandler(w http.ResponseWriter, r *http.Request) { fmt.Printf("app.FetchCustomRecord 出错: %s\n", err) } } else { - CustomRecord, err = app.FetchCustomRecord(message.UserID) + CustomRecord, err = app.FetchCustomRecord(message.UserID + message.SelfID) if err != nil { fmt.Printf("app.FetchCustomRecord 出错: %s\n", err) }