Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
Vanessa 2023-03-05 13:01:59 +08:00
commit 5e9f8956ff
12 changed files with 318 additions and 102 deletions

View File

@ -6,24 +6,24 @@
"20200924095938-a9p5450": 2,
"20200924100110-vcg96wy": 1,
"20200924100635-ms0p9lb": 6,
"20200924100717-yzwzn64": 18,
"20200924100717-yzwzn64": 19,
"20200924100744-br924ar": 8,
"20200924100808-j9sddk9": 2,
"20200924100906-0u4zfq3": 4,
"20200924100950-9op5xi1": 15,
"20200924100950-9op5xi1": 16,
"20200924101106-19z4kaa": 1,
"20200924101200-gss5vee": 4,
"20200924101225-k254i8g": 2,
"20200924101256-f8b1sbi": 3,
"20201004194026-s8h2cog": 16,
"20201004194026-s8h2cog": 17,
"20201117112518-dott91x": 6,
"20201121224345-rc27qvo": 5,
"20201204184532-3qm9l8n": 9,
"20201210233038-3xr19g5": 5,
"20201222100222-q47d64s": 2,
"20201222100339-i5hzcph": 1,
"20201227201128-m1wrouw": 17,
"20201227201751-gv0fpx2": 19,
"20201227201128-m1wrouw": 18,
"20201227201751-gv0fpx2": 20,
"20210110181011-fbhoesf": 5,
"20210117215840-jcl17fx": 3,
"20210127203829-qe2mzof": 10,
@ -47,6 +47,7 @@
"20220708103401-mgydrfg": 3,
"20221016204105-qx2aq0g": 3,
"20221223221636-ms2b4w9": 14,
"20230104152135-1iei0xa": 20,
"20230106104821-9nfphwm": 1
"20230104152135-1iei0xa": 21,
"20230106104821-9nfphwm": 1,
"20230304000547-ibldj1z": 15
}

View File

@ -5,7 +5,7 @@
"Properties": {
"id": "20230304000547-ibldj1z",
"title": "Artificial Intelligence",
"updated": "20230304000723"
"updated": "20230305102525"
},
"Children": [
{
@ -70,7 +70,7 @@
},
"Properties": {
"id": "20230304000646-a5e7srt",
"updated": "20230304000646"
"updated": "20230305102525"
},
"Children": [
{
@ -114,7 +114,7 @@
},
"Properties": {
"id": "20230304000646-6207b6r",
"updated": "20230304000646"
"updated": "20230305102525"
},
"Children": [
{
@ -137,7 +137,7 @@
"ListData": {},
"Properties": {
"id": "20230304000646-aj5szch",
"updated": "20230304000646"
"updated": "20230305102525"
},
"Children": [
{
@ -186,7 +186,7 @@
},
"Properties": {
"id": "20230304000646-78w2ck5",
"updated": "20230304000646"
"updated": "20230305102525"
},
"Children": [
{
@ -194,7 +194,7 @@
"Type": "NodeParagraph",
"Properties": {
"id": "20230304000646-2blt9e0",
"updated": "20230304000646"
"updated": "20230305102525"
},
"Children": [
{
@ -208,7 +208,7 @@
},
{
"Type": "NodeText",
"Data": " specifies the timeout time (seconds), the default is 15 seconds (optional)"
"Data": " specifies the timeout time (seconds), the default is 30 seconds (optional)"
}
]
}
@ -260,7 +260,7 @@
},
"Properties": {
"id": "20230304000646-8swnrqn",
"updated": "20230304000646"
"updated": "20230304171533"
},
"Children": [
{
@ -268,7 +268,7 @@
"Type": "NodeParagraph",
"Properties": {
"id": "20230304000646-33hvl2k",
"updated": "20230304000646"
"updated": "20230304171533"
},
"Children": [
{
@ -282,7 +282,7 @@
},
{
"Type": "NodeText",
"Data": " specifies the number of tokens, the default is 0, that is unlimited (optional)"
"Data": " specifies the number of tokens, the default is 0, that is, the default value of the model is used (optional)"
}
]
}

View File

@ -1,6 +1,6 @@
{
"20200812220555-lj3enxa": 1,
"20200813004551-gm0pbn1": 15,
"20200813004551-gm0pbn1": 16,
"20200813004931-q4cu8na": 1,
"20200813013559-sgbzl5k": 3,
"20200813093015-u6bopdt": 2,
@ -9,20 +9,20 @@
"20200813163359-v04n73b": 4,
"20200822191536-rm6hwid": 4,
"20200825162036-4dx365o": 1,
"20200828105441-r76vmu5": 18,
"20200828105441-r76vmu5": 19,
"20200905090211-2vixtlf": 2,
"20200910201551-h4twhas": 6,
"20200915214115-42b8zma": 8,
"20200922102318-oz84yu3": 2,
"20201004184819-nj8ibyg": 16,
"20201004184819-nj8ibyg": 17,
"20201117101902-2ewjjum": 6,
"20201121212605-9td1a62": 5,
"20201204181006-7bkppue": 9,
"20201210103036-1x3vm8t": 5,
"20201222093044-rx4zjoy": 1,
"20201222095049-hghafhe": 2,
"20201227173504-847cs1q": 17,
"20201227194925-7ipoiv6": 19,
"20201227173504-847cs1q": 18,
"20201227194925-7ipoiv6": 20,
"20210110175347-2xrwoiq": 5,
"20210117211155-56n4odu": 3,
"20210127202655-2334vvv": 10,
@ -48,6 +48,7 @@
"20220708095345-tu7nz95": 3,
"20221016213308-uz5af79": 3,
"20221223215557-o6gfsoy": 14,
"20230104144904-39br4c6": 20,
"20230106101434-e6g4av3": 1
"20230104144904-39br4c6": 21,
"20230106101434-e6g4av3": 1,
"20230303235619-ex5l63e": 15
}

View File

@ -5,7 +5,7 @@
"Properties": {
"id": "20230303235619-ex5l63e",
"title": "人工智能",
"updated": "20230304000753"
"updated": "20230305102554"
},
"Children": [
{
@ -70,7 +70,7 @@
},
"Properties": {
"id": "20230303235749-kurfjky",
"updated": "20230304000517"
"updated": "20230305102554"
},
"Children": [
{
@ -113,7 +113,8 @@
"Num": 2
},
"Properties": {
"id": "20230303235819-0o648f9"
"id": "20230303235819-0o648f9",
"updated": "20230305102554"
},
"Children": [
{
@ -136,7 +137,7 @@
"ListData": {},
"Properties": {
"id": "20230303235950-z00ou0r",
"updated": "20230303235950"
"updated": "20230305102554"
},
"Children": [
{
@ -185,7 +186,7 @@
},
"Properties": {
"id": "20230303235950-l6o3a5a",
"updated": "20230303235950"
"updated": "20230305102554"
},
"Children": [
{
@ -193,7 +194,7 @@
"Type": "NodeParagraph",
"Properties": {
"id": "20230303235950-zfhft7e",
"updated": "20230303235950"
"updated": "20230305102554"
},
"Children": [
{
@ -207,7 +208,7 @@
},
{
"Type": "NodeText",
"Data": " 指定超时时间(秒),默认为 15 秒(可选)"
"Data": " 指定超时时间(秒),默认为 30 秒(可选)"
}
]
}
@ -259,7 +260,7 @@
},
"Properties": {
"id": "20230303235950-5y60wvh",
"updated": "20230303235950"
"updated": "20230304171349"
},
"Children": [
{
@ -267,7 +268,7 @@
"Type": "NodeParagraph",
"Properties": {
"id": "20230303235950-ffevwk8",
"updated": "20230303235950"
"updated": "20230304171349"
},
"Children": [
{
@ -281,7 +282,11 @@
},
{
"Type": "NodeText",
"Data": " 指定 tokens 数量,默认为 0即不限制可选"
"Data": " 指定 tokens 数量,默认为 0即使用模型默认值"
},
{
"Type": "NodeText",
"Data": "(可选)"
}
]
}

View File

@ -18,14 +18,14 @@
"20211226121203-rjjngpz": 6,
"20211226121232-23s79xr": 7,
"20211226121319-emrk2yy": 1,
"20211226121329-c5v3dto": 19,
"20211226121329-c5v3dto": 20,
"20211226121332-irgblss": 4,
"20211226121438-xaafdo8": 2,
"20211226121503-k3jma6m": 1,
"20211226122358-hctqcn5": 18,
"20211226122459-08mi5cq": 17,
"20211226122523-rl8356a": 16,
"20211226122549-jktxego": 15,
"20211226122358-hctqcn5": 19,
"20211226122459-08mi5cq": 18,
"20211226122523-rl8356a": 17,
"20211226122549-jktxego": 16,
"20211226122707-8cr09co": 13,
"20211226122728-cnqf7rz": 12,
"20211226122814-r1rdpcx": 11,
@ -43,6 +43,7 @@
"20220708102441-u6wopo9": 3,
"20221016213639-1nag9jj": 3,
"20221223221501-mops33i": 14,
"20230104151953-48hwkwf": 20,
"20230106104645-o838uew": 1
"20230104151953-48hwkwf": 21,
"20230106104645-o838uew": 1,
"20230304000829-9jwu3po": 15
}

View File

@ -5,7 +5,7 @@
"Properties": {
"id": "20230304000829-9jwu3po",
"title": "人工智能",
"updated": "20230304000912"
"updated": "20230305102546"
},
"Children": [
{
@ -70,7 +70,7 @@
},
"Properties": {
"id": "20230304000837-t0swnf6",
"updated": "20230304000837"
"updated": "20230305102546"
},
"Children": [
{
@ -114,7 +114,7 @@
},
"Properties": {
"id": "20230304000837-ktv65zx",
"updated": "20230304000837"
"updated": "20230305102546"
},
"Children": [
{
@ -137,7 +137,7 @@
"ListData": {},
"Properties": {
"id": "20230304000837-8z180b1",
"updated": "20230304000837"
"updated": "20230305102546"
},
"Children": [
{
@ -186,7 +186,7 @@
},
"Properties": {
"id": "20230304000837-71slt0q",
"updated": "20230304000837"
"updated": "20230305102546"
},
"Children": [
{
@ -194,7 +194,7 @@
"Type": "NodeParagraph",
"Properties": {
"id": "20230304000837-t2um8y2",
"updated": "20230304000837"
"updated": "20230305102546"
},
"Children": [
{
@ -208,7 +208,7 @@
},
{
"Type": "NodeText",
"Data": " 指定超時時間(秒),默認為 15 秒(可選)"
"Data": " 指定超時時間(秒),默認為 30 秒(可選)"
}
]
}
@ -260,7 +260,7 @@
},
"Properties": {
"id": "20230304000837-sd6h4qn",
"updated": "20230304000837"
"updated": "20230304171553"
},
"Children": [
{
@ -268,7 +268,7 @@
"Type": "NodeParagraph",
"Properties": {
"id": "20230304000837-y8kj8ki",
"updated": "20230304000837"
"updated": "20230304171553"
},
"Children": [
{
@ -282,7 +282,7 @@
},
{
"Type": "NodeText",
"Data": " 指定 tokens 數量,默認為 0即不限制(可選)"
"Data": " 指定 tokens 數量,默認為 0即使用模型默認值(可選)"
}
]
}

View File

@ -17,11 +17,11 @@
package api
import (
"github.com/siyuan-note/siyuan/kernel/model"
"net/http"
"github.com/88250/gulu"
"github.com/gin-gonic/gin"
"github.com/siyuan-note/siyuan/kernel/model"
"github.com/siyuan-note/siyuan/kernel/util"
)
@ -35,10 +35,57 @@ func chatGPT(c *gin.Context) {
}
msg := arg["msg"].(string)
if "" == util.OpenAIAPIKey {
util.PushMsg(model.Conf.Language(193), 5000)
ret.Data = model.ChatGPT(msg)
}
func chatGPTContinueWriteBlocks(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
ret.Data = util.ChatGPT(msg)
idsArg := arg["ids"].([]interface{})
var ids []string
for _, id := range idsArg {
ids = append(ids, id.(string))
}
ret.Data = model.ChatGPTContinueWriteBlocks(ids)
}
func chatGPTTranslate(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
idsArg := arg["ids"].([]interface{})
var ids []string
for _, id := range idsArg {
ids = append(ids, id.(string))
}
lang := arg["lang"].(string)
ret.Data = model.ChatGPTTranslate(ids, lang)
}
func chatGPTSummary(c *gin.Context) {
ret := gulu.Ret.NewResult()
defer c.JSON(http.StatusOK, ret)
arg, ok := util.JsonArg(c, ret)
if !ok {
return
}
idsArg := arg["ids"].([]interface{})
var ids []string
for _, id := range idsArg {
ids = append(ids, id.(string))
}
ret.Data = model.ChatGPTSummary(ids)
}

View File

@ -329,4 +329,7 @@ func ServeAPI(ginServer *gin.Engine) {
ginServer.Handle("POST", "/api/av/renderAttributeView", model.CheckAuth, renderAttributeView)
ginServer.Handle("POST", "/api/ai/chatGPT", model.CheckAuth, chatGPT)
ginServer.Handle("POST", "/api/ai/chatGPTContinueWriteBlocks", model.CheckAuth, chatGPTContinueWriteBlocks)
ginServer.Handle("POST", "/api/ai/chatGPTTranslate", model.CheckAuth, chatGPTTranslate)
ginServer.Handle("POST", "/api/ai/chatGPTSummary", model.CheckAuth, chatGPTSummary)
}

View File

@ -198,8 +198,10 @@ func setWorkspaceDir(c *gin.Context) {
}
if gulu.OS.IsWindows() {
installDir := filepath.Dir(util.WorkingDir)
if strings.HasPrefix(path, installDir) {
// 改进判断工作空间路径实现 https://github.com/siyuan-note/siyuan/issues/7569
installDirLower := strings.ToLower(filepath.Dir(util.WorkingDir))
pathLower := strings.ToLower(path)
if strings.HasPrefix(pathLower, installDirLower) && util.IsSubPath(installDirLower, pathLower) {
ret.Code = -1
ret.Msg = model.Conf.Language(98)
ret.Data = map[string]interface{}{"closeTimeout": 5000}

111
kernel/model/ai.go Normal file
View File

@ -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 model
import (
"bytes"
"github.com/88250/lute/ast"
"github.com/88250/lute/parse"
"github.com/siyuan-note/siyuan/kernel/treenode"
"github.com/siyuan-note/siyuan/kernel/util"
)
func ChatGPTSummary(ids []string) (ret string) {
if !isOpenAIAPIEnabled() {
return
}
msg := getBlocksContent(ids)
ret = util.ChatGPTSummary(msg, Conf.Lang)
return
}
func ChatGPTTranslate(ids []string, lang string) (ret string) {
if !isOpenAIAPIEnabled() {
return
}
msg := getBlocksContent(ids)
ret = util.ChatGPTTranslate(msg, lang)
return
}
func ChatGPTContinueWriteBlocks(ids []string) (ret string) {
if !isOpenAIAPIEnabled() {
return
}
msg := getBlocksContent(ids)
ret, _ = util.ChatGPTContinueWrite(msg, nil)
return
}
func ChatGPT(msg string) (ret string) {
if !isOpenAIAPIEnabled() {
return
}
return util.ChatGPT(msg)
}
func isOpenAIAPIEnabled() bool {
if "" == util.OpenAIAPIKey {
util.PushMsg(Conf.Language(193), 5000)
return false
}
return true
}
func getBlocksContent(ids []string) string {
var nodes []*ast.Node
trees := map[string]*parse.Tree{}
for _, id := range ids {
bt := treenode.GetBlockTree(id)
if nil == bt {
continue
}
var tree *parse.Tree
if tree = trees[bt.RootID]; nil == tree {
tree, _ = loadTreeByBlockID(bt.RootID)
if nil == tree {
continue
}
trees[bt.RootID] = tree
}
if node := treenode.GetNodeInTree(tree, id); nil != node {
if ast.NodeDocument == node.Type {
for child := node.FirstChild; nil != child; child = child.Next {
nodes = append(nodes, child)
}
} else {
nodes = append(nodes, node)
}
}
}
buf := bytes.Buffer{}
for _, node := range nodes {
content := treenode.NodeStaticContent(node, nil, true)
buf.WriteString(content)
buf.WriteString("\n\n")
}
return buf.String()
}

View File

@ -148,13 +148,13 @@ func FilterFileName(name string) string {
return name
}
func IsSubFolder(parent, sub string) bool {
if 1 > len(parent) || 1 > len(sub) {
func IsSubPath(absPath, toCheckPath string) bool {
if 1 > len(absPath) || 1 > len(toCheckPath) {
return false
}
if gulu.OS.IsWindows() {
if filepath.IsAbs(parent) && filepath.IsAbs(sub) {
if strings.ToLower(parent)[0] != strings.ToLower(sub)[0] {
if filepath.IsAbs(absPath) && filepath.IsAbs(toCheckPath) {
if strings.ToLower(absPath)[0] != strings.ToLower(toCheckPath)[0] {
// 不在一个盘
return false
}
@ -162,7 +162,7 @@ func IsSubFolder(parent, sub string) bool {
}
up := ".." + string(os.PathSeparator)
rel, err := filepath.Rel(parent, sub)
rel, err := filepath.Rel(absPath, toCheckPath)
if err != nil {
return false
}

View File

@ -19,9 +19,6 @@ package util
import (
"bytes"
"context"
"errors"
"github.com/88250/lute/html"
"io"
"net/http"
"net/url"
"os"
@ -35,12 +32,32 @@ import (
var (
OpenAIAPIKey = ""
OpenAIAPITimeout = 15 * time.Second
OpenAIAPITimeout = 30 * time.Second
OpenAIAPIProxy = ""
OpenAIAPIMaxTokens = 0
)
var cachedContextMsg []string
func ChatGPT(msg string) (ret string) {
ret, retCtxMsgs := ChatGPTContinueWrite(msg, cachedContextMsg)
cachedContextMsg = append(cachedContextMsg, retCtxMsgs...)
return
}
func ChatGPTTranslate(msg string, lang string) (ret string) {
msg = "Translate to " + lang + ":\n" + msg
ret, _ = ChatGPTContinueWrite(msg, nil)
return
}
func ChatGPTSummary(msg string, lang string) (ret string) {
msg = "Summarized as follows, the result is in {" + lang + "}:\n" + msg
ret, _ = ChatGPTContinueWrite(msg, nil)
return
}
func ChatGPTContinueWrite(msg string, contextMsgs []string) (ret string, retContextMsgs []string) {
if "" == OpenAIAPIKey {
return
}
@ -48,54 +65,69 @@ func ChatGPT(msg string) (ret string) {
PushEndlessProgress("Requesting...")
defer ClearPushProgress(100)
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 := newOpenAIClient()
buf := &bytes.Buffer{}
for i := 0; i < 7; i++ {
part, stop := chatGPT(msg, contextMsgs, c)
buf.WriteString(part)
if stop {
break
}
PushEndlessProgress("Continue requesting...")
}
c := gogpt.NewClientWithConfig(config)
ctx, cancel := context.WithTimeout(context.Background(), OpenAIAPITimeout)
defer cancel()
ret = buf.String()
ret = strings.TrimSpace(ret)
retContextMsgs = append(retContextMsgs, msg, ret)
return
}
func chatGPT(msg string, contextMsgs []string, c *gogpt.Client) (ret string, stop bool) {
var reqMsgs []gogpt.ChatCompletionMessage
if 7 < len(contextMsgs) {
contextMsgs = contextMsgs[len(contextMsgs)-7:]
}
for _, ctxMsg := range contextMsgs {
reqMsgs = append(reqMsgs, gogpt.ChatCompletionMessage{
Role: "user",
Content: ctxMsg,
})
}
reqMsgs = append(reqMsgs, gogpt.ChatCompletionMessage{
Role: "user",
Content: msg,
})
req := gogpt.ChatCompletionRequest{
Model: gogpt.GPT3Dot5Turbo,
MaxTokens: OpenAIAPIMaxTokens,
Messages: []gogpt.ChatCompletionMessage{
{
Role: "user",
Content: msg,
},
},
Messages: reqMsgs,
}
stream, err := c.CreateChatCompletionStream(ctx, req)
ctx, cancel := context.WithTimeout(context.Background(), OpenAIAPITimeout)
defer cancel()
resp, err := c.CreateChatCompletion(ctx, req)
if nil != err {
logging.LogErrorf("create chat completion stream failed: %s", err)
PushErrMsg("Requesting failed, please check kernel log for more details", 3000)
logging.LogErrorf("create chat completion failed: %s", err)
stop = true
return
}
defer stream.Close()
buf := bytes.Buffer{}
for {
resp, recvErr := stream.Recv()
if errors.Is(recvErr, io.EOF) {
break
}
if 1 > len(resp.Choices) {
stop = true
return
}
if nil != recvErr {
logging.LogErrorf("create chat completion stream recv failed: %s", recvErr)
break
}
for _, choice := range resp.Choices {
content := choice.Delta.Content
buf.WriteString(content)
PushEndlessProgress(html.EscapeHTMLStr(buf.String()))
}
buf := &strings.Builder{}
choice := resp.Choices[0]
buf.WriteString(choice.Message.Content)
if "length" == choice.FinishReason {
stop = false
} else {
stop = true
}
ret = buf.String()
@ -103,6 +135,19 @@ func ChatGPT(msg string) (ret string) {
return
}
func newOpenAIClient() *gogpt.Client {
config := gogpt.DefaultConfig(OpenAIAPIKey)
if "" != OpenAIAPIProxy {
proxyUrl, err := url.Parse(OpenAIAPIProxy)
if nil != err {
logging.LogErrorf("OpenAI API proxy failed: %v", err)
} else {
config.HTTPClient = &http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(proxyUrl)}}
}
}
return gogpt.NewClientWithConfig(config)
}
func initOpenAI() {
OpenAIAPIKey = os.Getenv("SIYUAN_OPENAI_API_KEY")
if "" == OpenAIAPIKey {