فهرست منبع

:sparkles: 桌面端接入 OpenAI Chat API https://github.com/siyuan-note/siyuan/issues/7560

Liang Ding 2 سال پیش
والد
کامیت
49008041b5
6فایلهای تغییر یافته به همراه156 افزوده شده و 0 حذف شده
  1. 38 0
      kernel/api/ai.go
  2. 2 0
      kernel/api/router.go
  3. 1 0
      kernel/go.mod
  4. 2 0
      kernel/go.sum
  5. 111 0
      kernel/util/openai.go
  6. 2 0
      kernel/util/working.go

+ 38 - 0
kernel/api/ai.go

@@ -0,0 +1,38 @@
+// SiYuan - Build Your Eternal Digital Garden
+// Copyright (c) 2020-present, b3log.org
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+package api
+
+import (
+	"net/http"
+
+	"github.com/88250/gulu"
+	"github.com/gin-gonic/gin"
+	"github.com/siyuan-note/siyuan/kernel/util"
+)
+
+func chatGPT(c *gin.Context) {
+	ret := gulu.Ret.NewResult()
+	defer c.JSON(http.StatusOK, ret)
+
+	arg, ok := util.JsonArg(c, ret)
+	if !ok {
+		return
+	}
+
+	msg := arg["msg"].(string)
+	ret.Data = util.ChatGPT(msg)
+}

+ 2 - 0
kernel/api/router.go

@@ -327,4 +327,6 @@ func ServeAPI(ginServer *gin.Engine) {
 	ginServer.Handle("GET", "/snippets/*filepath", serveSnippets)
 
 	ginServer.Handle("POST", "/api/av/renderAttributeView", model.CheckAuth, renderAttributeView)
+
+	ginServer.Handle("POST", "/api/ai/chatGPT", model.CheckAuth, chatGPT)
 }

+ 1 - 0
kernel/go.mod

@@ -40,6 +40,7 @@ require (
 	github.com/panjf2000/ants/v2 v2.7.1
 	github.com/patrickmn/go-cache v2.1.0+incompatible
 	github.com/radovskyb/watcher v1.0.7
+	github.com/sashabaranov/go-gpt3 v1.3.3
 	github.com/shirou/gopsutil/v3 v3.23.1
 	github.com/siyuan-note/dejavu v0.0.0-20230223101417-691d54268764
 	github.com/siyuan-note/encryption v0.0.0-20220713091850-5ecd92177b75

+ 2 - 0
kernel/go.sum

@@ -266,6 +266,8 @@ github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUA
 github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
 github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI=
 github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs=
+github.com/sashabaranov/go-gpt3 v1.3.3 h1:S8Zd4YybnBaNMK+w+XGGWgsjQY1R+6QE2n9SLzVna9k=
+github.com/sashabaranov/go-gpt3 v1.3.3/go.mod h1:BIZdbwdzxZbCrcKGMGH6u2eyGe1xFuX9Anmh3tCP8lQ=
 github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg=
 github.com/shirou/gopsutil/v3 v3.23.1 h1:a9KKO+kGLKEvcPIs4W62v0nu3sciVDOOOPUD0Hz7z/4=
 github.com/shirou/gopsutil/v3 v3.23.1/go.mod h1:NN6mnm5/0k8jw4cBfCnJtr5L7ErOTg18tMNpgFkn0hA=

+ 111 - 0
kernel/util/openai.go

@@ -0,0 +1,111 @@
+// SiYuan - Build Your Eternal Digital Garden
+// Copyright (c) 2020-present, b3log.org
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+package util
+
+import (
+	"context"
+	"net/http"
+	"net/url"
+	"os"
+	"strconv"
+	"strings"
+	"time"
+
+	gogpt "github.com/sashabaranov/go-gpt3"
+	"github.com/siyuan-note/logging"
+)
+
+var (
+	OpenAIAPIKey       = ""
+	OpenAIAPITimeout   = 15 * time.Second
+	OpenAIAPIProxy     = ""
+	OpenAIAPIMaxTokens = 4096
+)
+
+func ChatGPT(msg string) (ret string) {
+	if "" == OpenAIAPIKey {
+		return
+	}
+
+	config := gogpt.DefaultConfig(OpenAIAPIKey)
+	if "" != OpenAIAPIProxy {
+		proxyUrl, err := url.Parse(OpenAIAPIProxy)
+		if nil != err {
+			logging.LogErrorf("OpenAI API proxy error: %v", err)
+		} else {
+			config.HTTPClient = &http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(proxyUrl)}}
+		}
+	}
+
+	c := gogpt.NewClientWithConfig(config)
+	ctx, cancel := context.WithTimeout(context.Background(), OpenAIAPITimeout)
+	defer cancel()
+	req := gogpt.ChatCompletionRequest{
+		Model:     gogpt.GPT3Dot5Turbo,
+		MaxTokens: OpenAIAPIMaxTokens,
+		Messages: []gogpt.ChatCompletionMessage{
+			{
+				Role:    "user",
+				Content: msg,
+			},
+		},
+	}
+	resp, err := c.CreateChatCompletion(ctx, req)
+	if nil != err {
+		logging.LogErrorf("create chat completion failed: %s", err)
+		return
+	}
+
+	if 0 < len(resp.Choices) {
+		ret = resp.Choices[0].Message.Content
+		ret = strings.TrimSpace(ret)
+	}
+	return
+}
+
+func initOpenAI() {
+	OpenAIAPIKey = os.Getenv("SIYUAN_OPENAI_API_KEY")
+	if "" == OpenAIAPIKey {
+		return
+	}
+
+	timeout := os.Getenv("SIYUAN_OPENAI_API_TIMEOUT")
+	if "" != timeout {
+		timeoutInt, err := strconv.Atoi(timeout)
+		if nil == err {
+			OpenAIAPITimeout = time.Duration(timeoutInt) * time.Second
+		}
+	}
+
+	proxy := os.Getenv("SIYUAN_OPENAI_API_PROXY")
+	if "" != proxy {
+		OpenAIAPIProxy = proxy
+	}
+
+	maxTokens := os.Getenv("SIYUAN_OPENAI_API_MAX_TOKENS")
+	if "" != maxTokens {
+		maxTokensInt, err := strconv.Atoi(maxTokens)
+		if nil == err {
+			OpenAIAPIMaxTokens = maxTokensInt
+		}
+	}
+	if 1 > OpenAIAPIMaxTokens {
+		OpenAIAPIMaxTokens = 4096
+	}
+
+	logging.LogInfof("OpenAI API enabled [maxTokens=%d, timeout=%ds, proxy=%s]", OpenAIAPIMaxTokens, int(OpenAIAPITimeout.Seconds()), OpenAIAPIProxy)
+}

+ 2 - 0
kernel/util/working.go

@@ -118,6 +118,8 @@ func Boot() {
 	bootBanner := figure.NewColorFigure("SiYuan", "isometric3", "green", true)
 	logging.LogInfof("\n" + bootBanner.String())
 	logBootInfo()
+
+	initOpenAI()
 }
 
 func setBootDetails(details string) {