From 6a38de7eaa3555a75557b5a70e6e85a91b9edd74 Mon Sep 17 00:00:00 2001 From: RockYang Date: Sun, 26 Mar 2023 21:10:40 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=B7=BB=E5=8A=A0=E5=A4=9A?= =?UTF-8?q?=E4=B8=AA=20ChatGPT=20API=20=E4=BB=A3=E7=90=86=E5=9C=B0?= =?UTF-8?q?=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/chat_handler.go | 48 ++++++++++++++++++++++++-------- server/config_handler.go | 59 ++++++++++++++++++++++++++++++++++++---- server/server.go | 2 ++ types/chat.go | 4 +-- types/config.go | 5 ++-- web/src/views/Chat.vue | 2 +- 6 files changed, 99 insertions(+), 21 deletions(-) diff --git a/server/chat_handler.go b/server/chat_handler.go index 705ebdeb..5aa708d7 100644 --- a/server/chat_handler.go +++ b/server/chat_handler.go @@ -66,6 +66,7 @@ func (s *Server) sendMessage(sessionId string, role string, text string, ws Clie } else { context = s.Config.ChatRoles[role].Context } + logger.Infof("会话上下文:%+v", context) r.Messages = append(context, types.Message{ Role: "user", Content: text, @@ -78,17 +79,6 @@ func (s *Server) sendMessage(sessionId string, role string, text string, ws Clie // 创建 HttpClient 请求对象 var client *http.Client - if s.Config.ProxyURL == "" { - client = &http.Client{} - } else { // 使用代理 - uri := url.URL{} - proxy, _ := uri.Parse(s.Config.ProxyURL) - client = &http.Client{ - Transport: &http.Transport{ - Proxy: http.ProxyURL(proxy), - }, - } - } request, err := http.NewRequest(http.MethodPost, s.Config.Chat.ApiURL, bytes.NewBuffer(requestBody)) if err != nil { return err @@ -98,7 +88,20 @@ func (s *Server) sendMessage(sessionId string, role string, text string, ws Clie var retryCount = 3 var response *http.Response var failedKey = "" + var failedProxyURL = "" for retryCount > 0 { + proxyURL := s.getProxyURL(failedProxyURL) + if proxyURL == "" { + client = &http.Client{} + } else { // 使用代理 + uri := url.URL{} + proxy, _ := uri.Parse(proxyURL) + client = &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyURL(proxy), + }, + } + } apiKey := s.getApiKey(failedKey) if apiKey == "" { logger.Info("Too many requests, all Api Key is not available") @@ -113,6 +116,7 @@ func (s *Server) sendMessage(sessionId string, role string, text string, ws Clie } else { logger.Error(err) failedKey = apiKey + failedProxyURL = proxyURL } retryCount-- } @@ -214,6 +218,28 @@ func (s *Server) getApiKey(failedKey string) string { return "" } +// 获取一个可用的代理 +func (s *Server) getProxyURL(failedProxyURL string) string { + if len(s.Config.ProxyURL) == 0 { + return "" + } + + if len(s.Config.ProxyURL) == 1 || failedProxyURL == "" { + return s.Config.ProxyURL[0] + } + + for i, v := range s.Config.ProxyURL { + if failedProxyURL == v { + if i == len(s.Config.ProxyURL)-1 { + return s.Config.ProxyURL[0] + } else { + return s.Config.ProxyURL[i+1] + } + } + } + return "" +} + // 回复客户端消息 func replyMessage(message types.WsMessage, client Client) { msg, err := json.Marshal(message) diff --git a/server/config_handler.go b/server/config_handler.go index 25b063e0..f0a51c6b 100644 --- a/server/config_handler.go +++ b/server/config_handler.go @@ -19,11 +19,6 @@ func (s *Server) ConfigSetHandle(c *gin.Context) { return } - // proxy URL - if proxy, ok := data["proxy"]; ok { - s.Config.ProxyURL = proxy - } - // Model if model, ok := data["model"]; ok { s.Config.Chat.Model = model @@ -268,3 +263,57 @@ func (s *Server) UpdateChatRole(c *gin.Context) { c.JSON(http.StatusOK, types.BizVo{Code: types.Success, Message: types.OkMsg}) } + +// AddProxy 添加一个代理 +func (s *Server) AddProxy(c *gin.Context) { + var data map[string]string + err := json.NewDecoder(c.Request.Body).Decode(&data) + if err != nil { + logger.Errorf("Error decode json data: %s", err.Error()) + c.JSON(http.StatusBadRequest, nil) + return + } + + if proxy, ok := data["proxy"]; ok { + if !utils.ContainsItem(s.Config.ProxyURL, proxy) { + s.Config.ProxyURL = append(s.Config.ProxyURL, proxy) + } + } + + // 保存配置文件 + err = types.SaveConfig(s.Config, s.ConfigPath) + if err != nil { + c.JSON(http.StatusOK, types.BizVo{Code: types.Failed, Message: "Failed to save config file"}) + return + } + + c.JSON(http.StatusOK, types.BizVo{Code: types.Success, Message: types.OkMsg, Data: s.Config.ProxyURL}) +} + +func (s *Server) RemoveProxy(c *gin.Context) { + var data map[string]string + err := json.NewDecoder(c.Request.Body).Decode(&data) + if err != nil { + logger.Errorf("Error decode json data: %s", err.Error()) + c.JSON(http.StatusBadRequest, nil) + return + } + + if proxy, ok := data["proxy"]; ok { + for i, v := range s.Config.ProxyURL { + if v == proxy { + s.Config.ProxyURL = append(s.Config.ProxyURL[:i], s.Config.ProxyURL[i+1:]...) + break + } + } + } + + // 保存配置文件 + err = types.SaveConfig(s.Config, s.ConfigPath) + if err != nil { + c.JSON(http.StatusOK, types.BizVo{Code: types.Failed, Message: "Failed to save config file"}) + return + } + + c.JSON(http.StatusOK, types.BizVo{Code: types.Success, Message: types.OkMsg, Data: s.Config.ProxyURL}) +} diff --git a/server/server.go b/server/server.go index 0c7ac6a1..ba100fd8 100644 --- a/server/server.go +++ b/server/server.go @@ -81,6 +81,8 @@ func (s *Server) Run(webRoot embed.FS, path string, debug bool) { engine.POST("api/config/apikey/remove", s.RemoveApiKey) engine.POST("api/config/apikey/list", s.ListApiKeys) engine.POST("api/config/role/set", s.UpdateChatRole) + engine.POST("api/config/proxy/add", s.AddProxy) + engine.POST("api/config/proxy/remove", s.RemoveProxy) engine.NoRoute(func(c *gin.Context) { if c.Request.URL.Path == "/favicon.ico" { diff --git a/types/chat.go b/types/chat.go index f5ad8aa9..6df1fcab 100644 --- a/types/chat.go +++ b/types/chat.go @@ -59,7 +59,7 @@ func GetDefaultChatRole() map[string]ChatRole { Name: "老师", Context: []Message{ {Role: "user", Content: "从现在开始,你将扮演一个老师,你是一个始终用苏格拉底风格回答问题的导师。你绝不会直接给学生答案,总是提出恰当的问题来引导学生自己思考。你应该根据学生的兴趣和知识来调整你的问题,将问题分解为更简单的部分,直到它达到适合他们的水平。"}, - {Role: "assistant", Content: "好的,让我来尝试扮演一位苏格拉底式的老师。请问,你有什么想要探讨的问题或者话题吗?我会通过恰当的问题引导你思考和探索答案。"}, + {Role: "assistant", Content: "好的,让我来尝试扮演一位苏格拉底式的老师。请问,你有什么想要探讨的问题或者话题吗?我会通过恰当的问题引导你思考和探索答案,绝对不直接给出答案。"}, }, HelloMsg: "师者,传道受业解惑也。", Icon: "images/avatar/teacher.jpg", @@ -108,7 +108,7 @@ func GetDefaultChatRole() map[string]ChatRole { }, HelloMsg: "你好,我是中颂福的销售代表颂福。中颂福酒,好喝不上头,是人民的福酒。", Icon: "images/avatar/seller.jpg", - Enable: true, + Enable: false, }, "english_trainer": { diff --git a/types/config.go b/types/config.go index ec253fbc..f058f833 100644 --- a/types/config.go +++ b/types/config.go @@ -12,7 +12,7 @@ import ( type Config struct { Listen string Session Session - ProxyURL string + ProxyURL []string Chat Chat EnableAuth bool // 是否开启鉴权 Tokens []string // 授权的白名单列表 TODO: 后期要存储到 LevelDB 或者 Mysql 数据库 @@ -43,7 +43,8 @@ type Session struct { func NewDefaultConfig() *Config { return &Config{ - Listen: "0.0.0.0:5678", + Listen: "0.0.0.0:5678", + ProxyURL: make([]string, 0), Session: Session{ SecretKey: utils.RandString(64), diff --git a/web/src/views/Chat.vue b/web/src/views/Chat.vue index 5720bd95..f5d79c6f 100644 --- a/web/src/views/Chat.vue +++ b/web/src/views/Chat.vue @@ -220,7 +220,7 @@ export default defineComponent({ reader.onload = () => { const data = JSON.parse(String(reader.result)); // 过滤掉重复的打招呼信息 - if (data['is_hello_msg'] && this.chatData.length > 3) { + if (data['is_hello_msg'] && this.chatData.length > 1) { return }