diff --git a/kernel/api/ai.go b/kernel/api/ai.go
new file mode 100644
index 000000000..7e4a592ec
--- /dev/null
+++ b/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 .
+
+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)
+}
diff --git a/kernel/api/router.go b/kernel/api/router.go
index 32a28127f..32ec0addb 100644
--- a/kernel/api/router.go
+++ b/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)
}
diff --git a/kernel/go.mod b/kernel/go.mod
index 27b9851d3..21b189afb 100644
--- a/kernel/go.mod
+++ b/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
diff --git a/kernel/go.sum b/kernel/go.sum
index b05a3dd8c..b7d7c5a9f 100644
--- a/kernel/go.sum
+++ b/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=
diff --git a/kernel/util/openai.go b/kernel/util/openai.go
new file mode 100644
index 000000000..48db08080
--- /dev/null
+++ b/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 .
+
+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)
+}
diff --git a/kernel/util/working.go b/kernel/util/working.go
index fe3771968..f7df1f374 100644
--- a/kernel/util/working.go
+++ b/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) {