Ver Fonte

enhance: chat with LLM

Jacky há 1 ano atrás
pai
commit
642e21a260
40 ficheiros alterados com 555 adições e 261 exclusões
  1. 0 2
      .editorconfig
  2. 1 1
      api/cluster/environment.go
  3. 36 36
      api/config/get.go
  4. 124 115
      api/openai/openai.go
  5. 2 2
      api/openai/router.go
  6. 2 0
      api/sites/domain.go
  7. 1 0
      api/sites/sites.go
  8. 3 0
      api/streams/streams.go
  9. 1 1
      app/src/api/config.ts
  10. 1 0
      app/src/api/domain.ts
  11. 1 1
      app/src/api/openai.ts
  12. 1 0
      app/src/api/stream.ts
  13. 2 11
      app/src/components/ChatGPT/ChatGPT.vue
  14. 16 8
      app/src/language/en/app.po
  15. 16 8
      app/src/language/es/app.po
  16. 16 8
      app/src/language/fr_FR/app.po
  17. 16 8
      app/src/language/ko_KR/app.po
  18. 16 8
      app/src/language/messages.pot
  19. 16 8
      app/src/language/ru_RU/app.po
  20. 16 8
      app/src/language/vi_VN/app.po
  21. BIN
      app/src/language/zh_CN/app.mo
  22. 16 8
      app/src/language/zh_CN/app.po
  23. 16 8
      app/src/language/zh_TW/app.po
  24. 1 0
      app/src/views/config/Config.vue
  25. 5 6
      app/src/views/config/ConfigEdit.vue
  26. 3 0
      app/src/views/domain/DomainEdit.vue
  27. 2 0
      app/src/views/domain/DomainList.vue
  28. 2 3
      app/src/views/domain/components/RightSettings.vue
  29. 3 0
      app/src/views/stream/StreamEdit.vue
  30. 2 0
      app/src/views/stream/StreamList.vue
  31. 3 5
      app/src/views/stream/components/RightSettings.vue
  32. 102 0
      internal/chatbot/context.go
  33. 23 0
      internal/chatbot/context_test.go
  34. 22 0
      internal/chatbot/messages.go
  35. 26 0
      internal/chatbot/messages_test.go
  36. 1 1
      internal/config/config.go
  37. 25 0
      internal/helper/directory.go
  38. 11 0
      internal/helper/directory_test.go
  39. 2 2
      model/config_backup.go
  40. 3 3
      query/config_backups.gen.go

+ 0 - 2
.editorconfig

@@ -1,8 +1,6 @@
 root = true
 
 [*]
-block_comment_start = /*
-block_comment_end = */
 charset = utf-8
 indent_style = space
 indent_size = 2

+ 1 - 1
api/cluster/environment.go

@@ -23,7 +23,7 @@ func GetEnvironment(c *gin.Context) {
 		return
 	}
 
-	c.JSON(http.StatusOK, env)
+	c.JSON(http.StatusOK, analytic.GetNode(env))
 }
 
 func GetEnvironmentList(c *gin.Context) {

+ 36 - 36
api/config/get.go

@@ -1,52 +1,52 @@
 package config
 
 import (
-    "github.com/0xJacky/Nginx-UI/api"
-    "github.com/0xJacky/Nginx-UI/internal/config"
-    "github.com/0xJacky/Nginx-UI/internal/nginx"
-    "github.com/0xJacky/Nginx-UI/query"
-    "github.com/gin-gonic/gin"
-    "github.com/sashabaranov/go-openai"
-    "net/http"
-    "os"
+	"github.com/0xJacky/Nginx-UI/api"
+	"github.com/0xJacky/Nginx-UI/internal/config"
+	"github.com/0xJacky/Nginx-UI/internal/nginx"
+	"github.com/0xJacky/Nginx-UI/query"
+	"github.com/gin-gonic/gin"
+	"github.com/sashabaranov/go-openai"
+	"net/http"
+	"os"
 )
 
 func GetConfig(c *gin.Context) {
-    name := c.Param("name")
+	name := c.Param("name")
 
-    path := nginx.GetConfPath("/", name)
+	path := nginx.GetConfPath("/", name)
 
-    stat, err := os.Stat(path)
+	stat, err := os.Stat(path)
 
-    if err != nil {
-        api.ErrHandler(c, err)
-        return
-    }
+	if err != nil {
+		api.ErrHandler(c, err)
+		return
+	}
 
-    content, err := os.ReadFile(path)
+	content, err := os.ReadFile(path)
 
-    if err != nil {
-        api.ErrHandler(c, err)
-        return
-    }
+	if err != nil {
+		api.ErrHandler(c, err)
+		return
+	}
 
-    g := query.ChatGPTLog
-    chatgpt, err := g.Where(g.Name.Eq(path)).FirstOrCreate()
+	g := query.ChatGPTLog
+	chatgpt, err := g.Where(g.Name.Eq(path)).FirstOrCreate()
 
-    if err != nil {
-        api.ErrHandler(c, err)
-        return
-    }
+	if err != nil {
+		api.ErrHandler(c, err)
+		return
+	}
 
-    if chatgpt.Content == nil {
-        chatgpt.Content = make([]openai.ChatCompletionMessage, 0)
-    }
+	if chatgpt.Content == nil {
+		chatgpt.Content = make([]openai.ChatCompletionMessage, 0)
+	}
 
-    c.JSON(http.StatusOK, config.Config{
-        Name:            name,
-        Content:         string(content),
-        ChatGPTMessages: chatgpt.Content,
-        FilePath:        path,
-        ModifiedAt:      stat.ModTime(),
-    })
+	c.JSON(http.StatusOK, config.Config{
+		Name:            stat.Name(),
+		Content:         string(content),
+		ChatGPTMessages: chatgpt.Content,
+		FilePath:        path,
+		ModifiedAt:      stat.ModTime(),
+	})
 }

+ 124 - 115
api/openai/openai.go

@@ -1,123 +1,132 @@
 package openai
 
 import (
-    "context"
-    "crypto/tls"
-    "fmt"
-    "github.com/0xJacky/Nginx-UI/api"
-    "github.com/0xJacky/Nginx-UI/settings"
-    "github.com/gin-gonic/gin"
-    "github.com/pkg/errors"
-    "github.com/sashabaranov/go-openai"
-    "io"
-    "net/http"
-    "net/url"
-    "os"
+	"context"
+	"crypto/tls"
+	"fmt"
+	"github.com/0xJacky/Nginx-UI/api"
+	"github.com/0xJacky/Nginx-UI/internal/chatbot"
+	"github.com/0xJacky/Nginx-UI/settings"
+	"github.com/gin-gonic/gin"
+	"github.com/pkg/errors"
+	"github.com/sashabaranov/go-openai"
+	"io"
+	"net/http"
+	"net/url"
 )
 
-const ChatGPTInitPrompt = "You are a assistant who can help users write and optimise the configurations of Nginx, the first user message contains the content of the configuration file which is currently opened by the user and the current language code(CLC). You suppose to use the language corresponding to the CLC to give the first reply. Later the language environment depends on the user message. The first reply should involve the key information of the file and ask user what can you help them."
+const ChatGPTInitPrompt = `You are a assistant who can help users write and optimise the configurations of Nginx,
+the first user message contains the content of the configuration file which is currently opened by the user and
+the current language code(CLC). You suppose to use the language corresponding to the CLC to give the first reply.
+Later the language environment depends on the user message.
+The first reply should involve the key information of the file and ask user what can you help them.`
 
 func MakeChatCompletionRequest(c *gin.Context) {
-    var json struct {
-        Messages []openai.ChatCompletionMessage `json:"messages"`
-    }
-
-    if !api.BindAndValid(c, &json) {
-        return
-    }
-
-    messages := []openai.ChatCompletionMessage{
-        {
-            Role:    openai.ChatMessageRoleSystem,
-            Content: ChatGPTInitPrompt,
-        },
-    }
-    messages = append(messages, json.Messages...)
-    // sse server
-    c.Writer.Header().Set("Content-Type", "text/event-stream; charset=utf-8")
-    c.Writer.Header().Set("Cache-Control", "no-cache")
-    c.Writer.Header().Set("Connection", "keep-alive")
-    c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
-
-    config := openai.DefaultConfig(settings.OpenAISettings.Token)
-
-    if settings.OpenAISettings.Proxy != "" {
-        proxyUrl, err := url.Parse(settings.OpenAISettings.Proxy)
-        if err != nil {
-            c.Stream(func(w io.Writer) bool {
-                c.SSEvent("message", gin.H{
-                    "type":    "error",
-                    "content": err.Error(),
-                })
-                return false
-            })
-            return
-        }
-        transport := &http.Transport{
-            Proxy:           http.ProxyURL(proxyUrl),
-            TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
-        }
-        config.HTTPClient = &http.Client{
-            Transport: transport,
-        }
-    }
-
-    if settings.OpenAISettings.BaseUrl != "" {
-        config.BaseURL = settings.OpenAISettings.BaseUrl
-    }
-
-    openaiClient := openai.NewClientWithConfig(config)
-    ctx := context.Background()
-
-    req := openai.ChatCompletionRequest{
-        Model:    settings.OpenAISettings.Model,
-        Messages: messages,
-        Stream:   true,
-    }
-    stream, err := openaiClient.CreateChatCompletionStream(ctx, req)
-    if err != nil {
-        fmt.Printf("CompletionStream error: %v\n", err)
-        c.Stream(func(w io.Writer) bool {
-            c.SSEvent("message", gin.H{
-                "type":    "error",
-                "content": err.Error(),
-            })
-            return false
-        })
-        return
-    }
-    defer stream.Close()
-    msgChan := make(chan string)
-    go func() {
-        defer close(msgChan)
-        for {
-            response, err := stream.Recv()
-            if errors.Is(err, io.EOF) {
-                fmt.Println()
-                return
-            }
-
-            if err != nil {
-                fmt.Printf("Stream error: %v\n", err)
-                return
-            }
-
-            message := fmt.Sprintf("%s", response.Choices[0].Delta.Content)
-            fmt.Printf("%s", message)
-            _ = os.Stdout.Sync()
-
-            msgChan <- message
-        }
-    }()
-
-    c.Stream(func(w io.Writer) bool {
-        if m, ok := <-msgChan; ok {
-            c.SSEvent("message", gin.H{
-                "type":    "message",
-                "content": m,
-            })
-            return true
-        }
-        return false
-    })
+	var json struct {
+		Filepath string                         `json:"filepath"`
+		Messages []openai.ChatCompletionMessage `json:"messages"`
+	}
+
+	if !api.BindAndValid(c, &json) {
+		return
+	}
+
+	messages := []openai.ChatCompletionMessage{
+		{
+			Role:    openai.ChatMessageRoleSystem,
+			Content: ChatGPTInitPrompt,
+		},
+	}
+
+	messages = append(messages, json.Messages...)
+
+	if json.Filepath != "" {
+		messages = chatbot.ChatCompletionWithContext(json.Filepath, messages)
+	}
+
+	// SSE server
+	c.Writer.Header().Set("Content-Type", "text/event-stream; charset=utf-8")
+	c.Writer.Header().Set("Cache-Control", "no-cache")
+	c.Writer.Header().Set("Connection", "keep-alive")
+	c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
+
+	config := openai.DefaultConfig(settings.OpenAISettings.Token)
+
+	if settings.OpenAISettings.Proxy != "" {
+		proxyUrl, err := url.Parse(settings.OpenAISettings.Proxy)
+		if err != nil {
+			c.Stream(func(w io.Writer) bool {
+				c.SSEvent("message", gin.H{
+					"type":    "error",
+					"content": err.Error(),
+				})
+				return false
+			})
+			return
+		}
+		transport := &http.Transport{
+			Proxy:           http.ProxyURL(proxyUrl),
+			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+		}
+		config.HTTPClient = &http.Client{
+			Transport: transport,
+		}
+	}
+
+	if settings.OpenAISettings.BaseUrl != "" {
+		config.BaseURL = settings.OpenAISettings.BaseUrl
+	}
+
+	openaiClient := openai.NewClientWithConfig(config)
+	ctx := context.Background()
+
+	req := openai.ChatCompletionRequest{
+		Model:    settings.OpenAISettings.Model,
+		Messages: messages,
+		Stream:   true,
+	}
+	stream, err := openaiClient.CreateChatCompletionStream(ctx, req)
+	if err != nil {
+		fmt.Printf("CompletionStream error: %v\n", err)
+		c.Stream(func(w io.Writer) bool {
+			c.SSEvent("message", gin.H{
+				"type":    "error",
+				"content": err.Error(),
+			})
+			return false
+		})
+		return
+	}
+	defer stream.Close()
+	msgChan := make(chan string)
+	go func() {
+		defer close(msgChan)
+		for {
+			response, err := stream.Recv()
+			if errors.Is(err, io.EOF) {
+				fmt.Println()
+				return
+			}
+
+			if err != nil {
+				fmt.Printf("Stream error: %v\n", err)
+				return
+			}
+
+			message := fmt.Sprintf("%s", response.Choices[0].Delta.Content)
+
+			msgChan <- message
+		}
+	}()
+
+	c.Stream(func(w io.Writer) bool {
+		if m, ok := <-msgChan; ok {
+			c.SSEvent("message", gin.H{
+				"type":    "message",
+				"content": m,
+			})
+			return true
+		}
+		return false
+	})
 }

+ 2 - 2
api/openai/router.go

@@ -4,6 +4,6 @@ import "github.com/gin-gonic/gin"
 
 func InitRouter(r *gin.RouterGroup) {
 	// ChatGPT
-	r.POST("chat_gpt", MakeChatCompletionRequest)
-	r.POST("chat_gpt_record", StoreChatGPTRecord)
+	r.POST("chatgpt", MakeChatCompletionRequest)
+	r.POST("chatgpt_record", StoreChatGPTRecord)
 }

+ 2 - 0
api/sites/domain.go

@@ -132,6 +132,7 @@ func GetDomain(c *gin.Context) {
 			Config:          string(origContent),
 			AutoCert:        certModel.AutoCert == model.AutoCertEnabled,
 			ChatGPTMessages: chatgpt.Content,
+			Filepath:        path,
 		})
 		return
 	}
@@ -173,6 +174,7 @@ func GetDomain(c *gin.Context) {
 		AutoCert:        certModel.AutoCert == model.AutoCertEnabled,
 		CertInfo:        certInfoMap,
 		ChatGPTMessages: chatgpt.Content,
+		Filepath:        path,
 	})
 }
 

+ 1 - 0
api/sites/sites.go

@@ -17,4 +17,5 @@ type Site struct {
 	ChatGPTMessages []openai.ChatCompletionMessage `json:"chatgpt_messages,omitempty"`
 	Tokenized       *nginx.NgxConfig               `json:"tokenized,omitempty"`
 	CertInfo        map[int]*cert.Info             `json:"cert_info,omitempty"`
+	Filepath        string                         `json:"filepath"`
 }

+ 3 - 0
api/streams/streams.go

@@ -22,6 +22,7 @@ type Stream struct {
 	Config          string                         `json:"config"`
 	ChatGPTMessages []openai.ChatCompletionMessage `json:"chatgpt_messages,omitempty"`
 	Tokenized       *nginx.NgxConfig               `json:"tokenized,omitempty"`
+	Filepath        string                         `json:"filepath"`
 }
 
 func GetStreams(c *gin.Context) {
@@ -133,6 +134,7 @@ func GetStream(c *gin.Context) {
 			Name:            name,
 			Config:          string(origContent),
 			ChatGPTMessages: chatgpt.Content,
+			Filepath:        path,
 		})
 		return
 	}
@@ -152,6 +154,7 @@ func GetStream(c *gin.Context) {
 		Config:          nginxConfig.FmtCode(),
 		Tokenized:       nginxConfig,
 		ChatGPTMessages: chatgpt.Content,
+		Filepath:        path,
 	})
 }
 

+ 1 - 1
app/src/api/config.ts

@@ -5,7 +5,7 @@ export interface Config {
   name: string
   content: string
   chatgpt_messages: ChatComplicationMessage[]
-  file_path: string
+  filepath: string
   modified_at: string
 }
 

+ 1 - 0
app/src/api/domain.ts

@@ -10,6 +10,7 @@ export interface Site {
   advanced: boolean
   enabled: boolean
   name: string
+  filepath: string
   config: string
   auto_cert: boolean
   chatgpt_messages: ChatComplicationMessage[]

+ 1 - 1
app/src/api/openai.ts

@@ -8,7 +8,7 @@ export interface ChatComplicationMessage {
 
 const openai = {
   store_record(data: { file_name?: string; messages?: ChatComplicationMessage[] }) {
-    return http.post('/chat_gpt_record', data)
+    return http.post('/chatgpt_record', data)
   },
 }
 

+ 1 - 0
app/src/api/stream.ts

@@ -8,6 +8,7 @@ export interface Stream {
   advanced: boolean
   enabled: boolean
   name: string
+  filepath: string
   config: string
   chatgpt_messages: ChatComplicationMessage[]
   tokenized?: NgxConfig

+ 2 - 11
app/src/components/ChatGPT/ChatGPT.vue

@@ -49,26 +49,18 @@ async function request() {
 
   const { token } = storeToRefs(user)
 
-  console.log('fetching...')
-
   messages.value?.push(t.value)
 
   emit('update:history_messages', messages.value)
 
-  const res = await fetch(urlJoin(window.location.pathname, '/api/chat_gpt'), {
+  const res = await fetch(urlJoin(window.location.pathname, '/api/chatgpt'), {
     method: 'POST',
     headers: { Accept: 'text/event-stream', Authorization: token.value },
-    body: JSON.stringify({ messages: messages.value?.slice(0, messages.value?.length - 1) }),
+    body: JSON.stringify({ filepath: props.path, messages: messages.value?.slice(0, messages.value?.length - 1) }),
   })
 
-  // read body as stream
-  console.info('reading...')
-
   const reader = res.body!.getReader()
 
-  // read stream
-  console.info('reading stream...')
-
   let buffer = ''
 
   let hasCodeBlockIndicator = false
@@ -76,7 +68,6 @@ async function request() {
   while (true) {
     const { done, value } = await reader.read()
     if (done) {
-      console.info('done')
       setTimeout(() => {
         scrollToBottom()
       }, 500)

+ 16 - 8
app/src/language/en/app.po

@@ -175,7 +175,7 @@ msgstr "Base information"
 
 #: src/views/config/ConfigEdit.vue:116
 #: src/views/domain/components/RightSettings.vue:76
-#: src/views/preference/Preference.vue:93
+#: src/views/preference/Preference.vue:94
 #: src/views/stream/components/RightSettings.vue:76
 #, fuzzy
 msgid "Basic"
@@ -650,6 +650,10 @@ msgstr "Enable failed"
 msgid "Enable successfully"
 msgstr "Enabled successfully"
 
+#: src/views/preference/OpenAISettings.vue:67
+msgid "Enable this option will significantly increase the token usage."
+msgstr ""
+
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:175
 msgid "Enable TLS"
 msgstr "Enable TLS"
@@ -967,7 +971,7 @@ msgstr "Login successful"
 msgid "Logout successful"
 msgstr "Logout successful"
 
-#: src/views/preference/Preference.vue:111
+#: src/views/preference/Preference.vue:112
 msgid "Logrotate"
 msgstr ""
 
@@ -1097,7 +1101,7 @@ msgstr ""
 msgid "Next"
 msgstr "Next"
 
-#: src/views/preference/Preference.vue:99
+#: src/views/preference/Preference.vue:100
 msgid "Nginx"
 msgstr ""
 
@@ -1225,7 +1229,7 @@ msgstr ""
 msgid "Online"
 msgstr ""
 
-#: src/views/preference/Preference.vue:105
+#: src/views/preference/Preference.vue:106
 msgid "OpenAI"
 msgstr ""
 
@@ -1319,7 +1323,7 @@ msgstr ""
 msgid "Pre-release"
 msgstr ""
 
-#: src/routes/index.ts:239 src/views/preference/Preference.vue:88
+#: src/routes/index.ts:239 src/views/preference/Preference.vue:89
 msgid "Preference"
 msgstr ""
 
@@ -1472,7 +1476,7 @@ msgstr ""
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/ConfigEdit.vue:97 src/views/domain/DomainEdit.vue:260
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
-#: src/views/preference/Preference.vue:122 src/views/stream/StreamEdit.vue:251
+#: src/views/preference/Preference.vue:123 src/views/stream/StreamEdit.vue:251
 msgid "Save"
 msgstr "Save"
 
@@ -1488,7 +1492,7 @@ msgstr "Save error %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:45
-#: src/views/preference/Preference.vue:58
+#: src/views/preference/Preference.vue:59
 #, fuzzy
 msgid "Save successfully"
 msgstr "Saved successfully"
@@ -1513,6 +1517,10 @@ msgstr ""
 msgid "Send"
 msgstr "Send"
 
+#: src/views/preference/OpenAISettings.vue:66
+msgid "Send Include Context"
+msgstr ""
+
 #: src/components/NginxControl/NginxControl.vue:28
 #: src/components/NginxControl/NginxControl.vue:42
 #: src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts:46
@@ -1521,7 +1529,7 @@ msgstr "Send"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:182
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:221
 #: src/views/config/ConfigEdit.vue:41 src/views/domain/DomainList.vue:81
-#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:62
+#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:63
 #: src/views/stream/StreamList.vue:113 src/views/stream/StreamList.vue:81
 #: src/views/system/Upgrade.vue:42
 msgid "Server error"

+ 16 - 8
app/src/language/es/app.po

@@ -173,7 +173,7 @@ msgstr "Información general"
 
 #: src/views/config/ConfigEdit.vue:116
 #: src/views/domain/components/RightSettings.vue:76
-#: src/views/preference/Preference.vue:93
+#: src/views/preference/Preference.vue:94
 #: src/views/stream/components/RightSettings.vue:76
 msgid "Basic"
 msgstr "Básico"
@@ -623,6 +623,10 @@ msgstr "Falló la habilitación"
 msgid "Enable successfully"
 msgstr "Habilitado con Éxito"
 
+#: src/views/preference/OpenAISettings.vue:67
+msgid "Enable this option will significantly increase the token usage."
+msgstr ""
+
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:175
 msgid "Enable TLS"
 msgstr "Habilitar TLS"
@@ -925,7 +929,7 @@ msgstr "Acceso exitoso"
 msgid "Logout successful"
 msgstr "Cierre de sesión exitoso"
 
-#: src/views/preference/Preference.vue:111
+#: src/views/preference/Preference.vue:112
 msgid "Logrotate"
 msgstr ""
 
@@ -1049,7 +1053,7 @@ msgstr "Se liberó una nueva versión"
 msgid "Next"
 msgstr "Siguiente"
 
-#: src/views/preference/Preference.vue:99
+#: src/views/preference/Preference.vue:100
 msgid "Nginx"
 msgstr "Nginx"
 
@@ -1171,7 +1175,7 @@ msgstr "Una vez que se complete la verificación, los registros se eliminarán."
 msgid "Online"
 msgstr "En línea"
 
-#: src/views/preference/Preference.vue:105
+#: src/views/preference/Preference.vue:106
 msgid "OpenAI"
 msgstr "OpenAI"
 
@@ -1273,7 +1277,7 @@ msgstr "¡Seleccione al menos un nodo!"
 msgid "Pre-release"
 msgstr "Prelanzamiento"
 
-#: src/routes/index.ts:239 src/views/preference/Preference.vue:88
+#: src/routes/index.ts:239 src/views/preference/Preference.vue:89
 msgid "Preference"
 msgstr "Configuración"
 
@@ -1419,7 +1423,7 @@ msgstr "Corriendo"
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/ConfigEdit.vue:97 src/views/domain/DomainEdit.vue:260
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
-#: src/views/preference/Preference.vue:122 src/views/stream/StreamEdit.vue:251
+#: src/views/preference/Preference.vue:123 src/views/stream/StreamEdit.vue:251
 msgid "Save"
 msgstr "Guardar"
 
@@ -1435,7 +1439,7 @@ msgstr "Error al guardar %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:45
-#: src/views/preference/Preference.vue:58
+#: src/views/preference/Preference.vue:59
 msgid "Save successfully"
 msgstr "Guardado con éxito"
 
@@ -1459,6 +1463,10 @@ msgstr "Selector"
 msgid "Send"
 msgstr "Enviado"
 
+#: src/views/preference/OpenAISettings.vue:66
+msgid "Send Include Context"
+msgstr ""
+
 #: src/components/NginxControl/NginxControl.vue:28
 #: src/components/NginxControl/NginxControl.vue:42
 #: src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts:46
@@ -1467,7 +1475,7 @@ msgstr "Enviado"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:182
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:221
 #: src/views/config/ConfigEdit.vue:41 src/views/domain/DomainList.vue:81
-#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:62
+#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:63
 #: src/views/stream/StreamList.vue:113 src/views/stream/StreamList.vue:81
 #: src/views/system/Upgrade.vue:42
 msgid "Server error"

+ 16 - 8
app/src/language/fr_FR/app.po

@@ -176,7 +176,7 @@ msgstr "Information générale"
 
 #: src/views/config/ConfigEdit.vue:116
 #: src/views/domain/components/RightSettings.vue:76
-#: src/views/preference/Preference.vue:93
+#: src/views/preference/Preference.vue:94
 #: src/views/stream/components/RightSettings.vue:76
 msgid "Basic"
 msgstr "Basique"
@@ -648,6 +648,10 @@ msgstr "Échec de l'activation"
 msgid "Enable successfully"
 msgstr "Activé avec succès"
 
+#: src/views/preference/OpenAISettings.vue:67
+msgid "Enable this option will significantly increase the token usage."
+msgstr ""
+
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:175
 msgid "Enable TLS"
 msgstr "Activer TLS"
@@ -966,7 +970,7 @@ msgstr "Connexion réussie"
 msgid "Logout successful"
 msgstr "Déconnexion réussie"
 
-#: src/views/preference/Preference.vue:111
+#: src/views/preference/Preference.vue:112
 msgid "Logrotate"
 msgstr ""
 
@@ -1094,7 +1098,7 @@ msgstr "Nouvelle version publiée"
 msgid "Next"
 msgstr "Suivant"
 
-#: src/views/preference/Preference.vue:99
+#: src/views/preference/Preference.vue:100
 #, fuzzy
 msgid "Nginx"
 msgstr "Journal Nginx"
@@ -1220,7 +1224,7 @@ msgstr ""
 msgid "Online"
 msgstr ""
 
-#: src/views/preference/Preference.vue:105
+#: src/views/preference/Preference.vue:106
 msgid "OpenAI"
 msgstr "OpenAI"
 
@@ -1320,7 +1324,7 @@ msgstr ""
 msgid "Pre-release"
 msgstr ""
 
-#: src/routes/index.ts:239 src/views/preference/Preference.vue:88
+#: src/routes/index.ts:239 src/views/preference/Preference.vue:89
 msgid "Preference"
 msgstr "Préférence"
 
@@ -1475,7 +1479,7 @@ msgstr "En cours d'éxécution"
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/ConfigEdit.vue:97 src/views/domain/DomainEdit.vue:260
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
-#: src/views/preference/Preference.vue:122 src/views/stream/StreamEdit.vue:251
+#: src/views/preference/Preference.vue:123 src/views/stream/StreamEdit.vue:251
 msgid "Save"
 msgstr "Enregistrer"
 
@@ -1491,7 +1495,7 @@ msgstr "Enregistrer l'erreur %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:45
-#: src/views/preference/Preference.vue:58
+#: src/views/preference/Preference.vue:59
 msgid "Save successfully"
 msgstr "Sauvegarde réussie"
 
@@ -1515,6 +1519,10 @@ msgstr "Sélecteur"
 msgid "Send"
 msgstr "Envoyer"
 
+#: src/views/preference/OpenAISettings.vue:66
+msgid "Send Include Context"
+msgstr ""
+
 #: src/components/NginxControl/NginxControl.vue:28
 #: src/components/NginxControl/NginxControl.vue:42
 #: src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts:46
@@ -1523,7 +1531,7 @@ msgstr "Envoyer"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:182
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:221
 #: src/views/config/ConfigEdit.vue:41 src/views/domain/DomainList.vue:81
-#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:62
+#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:63
 #: src/views/stream/StreamList.vue:113 src/views/stream/StreamList.vue:81
 #: src/views/system/Upgrade.vue:42
 msgid "Server error"

+ 16 - 8
app/src/language/ko_KR/app.po

@@ -172,7 +172,7 @@ msgstr "기본 정보"
 
 #: src/views/config/ConfigEdit.vue:116
 #: src/views/domain/components/RightSettings.vue:76
-#: src/views/preference/Preference.vue:93
+#: src/views/preference/Preference.vue:94
 #: src/views/stream/components/RightSettings.vue:76
 msgid "Basic"
 msgstr "기본"
@@ -621,6 +621,10 @@ msgstr "활성화 실패"
 msgid "Enable successfully"
 msgstr "성공적으로 활성화"
 
+#: src/views/preference/OpenAISettings.vue:67
+msgid "Enable this option will significantly increase the token usage."
+msgstr ""
+
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:175
 msgid "Enable TLS"
 msgstr "TLS 활성화"
@@ -937,7 +941,7 @@ msgstr "로그인 성공"
 msgid "Logout successful"
 msgstr "로그아웃 성공"
 
-#: src/views/preference/Preference.vue:111
+#: src/views/preference/Preference.vue:112
 msgid "Logrotate"
 msgstr "로그관리"
 
@@ -1072,7 +1076,7 @@ msgstr "새 버전 출시"
 msgid "Next"
 msgstr "다음"
 
-#: src/views/preference/Preference.vue:99
+#: src/views/preference/Preference.vue:100
 msgid "Nginx"
 msgstr "Nginx"
 
@@ -1200,7 +1204,7 @@ msgstr "검증이 완료되면, 레코드는 제거됩니다."
 msgid "Online"
 msgstr "온라인"
 
-#: src/views/preference/Preference.vue:105
+#: src/views/preference/Preference.vue:106
 msgid "OpenAI"
 msgstr "오픈AI"
 
@@ -1296,7 +1300,7 @@ msgstr "적어도 하나의 노드를 선택해주세요!"
 msgid "Pre-release"
 msgstr "사전 출시"
 
-#: src/routes/index.ts:239 src/views/preference/Preference.vue:88
+#: src/routes/index.ts:239 src/views/preference/Preference.vue:89
 msgid "Preference"
 msgstr "환경설정"
 
@@ -1451,7 +1455,7 @@ msgstr "실행 중"
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/ConfigEdit.vue:97 src/views/domain/DomainEdit.vue:260
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
-#: src/views/preference/Preference.vue:122 src/views/stream/StreamEdit.vue:251
+#: src/views/preference/Preference.vue:123 src/views/stream/StreamEdit.vue:251
 msgid "Save"
 msgstr "저장"
 
@@ -1467,7 +1471,7 @@ msgstr "저장 오류 %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:45
-#: src/views/preference/Preference.vue:58
+#: src/views/preference/Preference.vue:59
 #, fuzzy
 msgid "Save successfully"
 msgstr "성공적으로 저장됨"
@@ -1492,6 +1496,10 @@ msgstr "선택"
 msgid "Send"
 msgstr "보내기"
 
+#: src/views/preference/OpenAISettings.vue:66
+msgid "Send Include Context"
+msgstr ""
+
 #: src/components/NginxControl/NginxControl.vue:28
 #: src/components/NginxControl/NginxControl.vue:42
 #: src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts:46
@@ -1500,7 +1508,7 @@ msgstr "보내기"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:182
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:221
 #: src/views/config/ConfigEdit.vue:41 src/views/domain/DomainList.vue:81
-#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:62
+#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:63
 #: src/views/stream/StreamList.vue:113 src/views/stream/StreamList.vue:81
 #: src/views/system/Upgrade.vue:42
 msgid "Server error"

+ 16 - 8
app/src/language/messages.pot

@@ -167,7 +167,7 @@ msgstr ""
 
 #: src/views/config/ConfigEdit.vue:116
 #: src/views/domain/components/RightSettings.vue:76
-#: src/views/preference/Preference.vue:93
+#: src/views/preference/Preference.vue:94
 #: src/views/stream/components/RightSettings.vue:76
 msgid "Basic"
 msgstr ""
@@ -621,6 +621,10 @@ msgstr ""
 msgid "Enable successfully"
 msgstr ""
 
+#: src/views/preference/OpenAISettings.vue:67
+msgid "Enable this option will significantly increase the token usage."
+msgstr ""
+
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:175
 msgid "Enable TLS"
 msgstr ""
@@ -931,7 +935,7 @@ msgstr ""
 msgid "Logout successful"
 msgstr ""
 
-#: src/views/preference/Preference.vue:111
+#: src/views/preference/Preference.vue:112
 msgid "Logrotate"
 msgstr ""
 
@@ -1049,7 +1053,7 @@ msgstr ""
 msgid "Next"
 msgstr ""
 
-#: src/views/preference/Preference.vue:99
+#: src/views/preference/Preference.vue:100
 msgid "Nginx"
 msgstr ""
 
@@ -1174,7 +1178,7 @@ msgstr ""
 msgid "Online"
 msgstr ""
 
-#: src/views/preference/Preference.vue:105
+#: src/views/preference/Preference.vue:106
 msgid "OpenAI"
 msgstr ""
 
@@ -1265,7 +1269,7 @@ msgid "Pre-release"
 msgstr ""
 
 #: src/routes/index.ts:239
-#: src/views/preference/Preference.vue:88
+#: src/views/preference/Preference.vue:89
 msgid "Preference"
 msgstr ""
 
@@ -1407,7 +1411,7 @@ msgstr ""
 #: src/views/config/ConfigEdit.vue:97
 #: src/views/domain/DomainEdit.vue:260
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
-#: src/views/preference/Preference.vue:122
+#: src/views/preference/Preference.vue:123
 #: src/views/stream/StreamEdit.vue:251
 msgid "Save"
 msgstr ""
@@ -1425,7 +1429,7 @@ msgstr ""
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:45
-#: src/views/preference/Preference.vue:58
+#: src/views/preference/Preference.vue:59
 msgid "Save successfully"
 msgstr ""
 
@@ -1450,6 +1454,10 @@ msgstr ""
 msgid "Send"
 msgstr ""
 
+#: src/views/preference/OpenAISettings.vue:66
+msgid "Send Include Context"
+msgstr ""
+
 #: src/components/NginxControl/NginxControl.vue:28
 #: src/components/NginxControl/NginxControl.vue:42
 #: src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts:46
@@ -1460,7 +1468,7 @@ msgstr ""
 #: src/views/config/ConfigEdit.vue:41
 #: src/views/domain/DomainList.vue:81
 #: src/views/other/Install.vue:70
-#: src/views/preference/Preference.vue:62
+#: src/views/preference/Preference.vue:63
 #: src/views/stream/StreamList.vue:113
 #: src/views/stream/StreamList.vue:81
 #: src/views/system/Upgrade.vue:42

+ 16 - 8
app/src/language/ru_RU/app.po

@@ -175,7 +175,7 @@ msgstr "Основная информация"
 
 #: src/views/config/ConfigEdit.vue:116
 #: src/views/domain/components/RightSettings.vue:76
-#: src/views/preference/Preference.vue:93
+#: src/views/preference/Preference.vue:94
 #: src/views/stream/components/RightSettings.vue:76
 #, fuzzy
 msgid "Basic"
@@ -652,6 +652,10 @@ msgstr "Включить не удалось"
 msgid "Enable successfully"
 msgstr "Активировано успешно"
 
+#: src/views/preference/OpenAISettings.vue:67
+msgid "Enable this option will significantly increase the token usage."
+msgstr ""
+
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:175
 msgid "Enable TLS"
 msgstr "Включить TLS"
@@ -971,7 +975,7 @@ msgstr "Авторизация успешна"
 msgid "Logout successful"
 msgstr "Выход выполнен успешно"
 
-#: src/views/preference/Preference.vue:111
+#: src/views/preference/Preference.vue:112
 msgid "Logrotate"
 msgstr ""
 
@@ -1101,7 +1105,7 @@ msgstr "Вышла новая версия"
 msgid "Next"
 msgstr "Дальше"
 
-#: src/views/preference/Preference.vue:99
+#: src/views/preference/Preference.vue:100
 #, fuzzy
 msgid "Nginx"
 msgstr "Журнал"
@@ -1230,7 +1234,7 @@ msgstr ""
 msgid "Online"
 msgstr ""
 
-#: src/views/preference/Preference.vue:105
+#: src/views/preference/Preference.vue:106
 msgid "OpenAI"
 msgstr ""
 
@@ -1326,7 +1330,7 @@ msgstr ""
 msgid "Pre-release"
 msgstr ""
 
-#: src/routes/index.ts:239 src/views/preference/Preference.vue:88
+#: src/routes/index.ts:239 src/views/preference/Preference.vue:89
 msgid "Preference"
 msgstr "Настройки"
 
@@ -1481,7 +1485,7 @@ msgstr "Выполняется"
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/ConfigEdit.vue:97 src/views/domain/DomainEdit.vue:260
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
-#: src/views/preference/Preference.vue:122 src/views/stream/StreamEdit.vue:251
+#: src/views/preference/Preference.vue:123 src/views/stream/StreamEdit.vue:251
 msgid "Save"
 msgstr "Сохранить"
 
@@ -1497,7 +1501,7 @@ msgstr "Ошибка сохранения %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:45
-#: src/views/preference/Preference.vue:58
+#: src/views/preference/Preference.vue:59
 #, fuzzy
 msgid "Save successfully"
 msgstr "Успешно сохранено"
@@ -1522,6 +1526,10 @@ msgstr "Выбор"
 msgid "Send"
 msgstr "Отправлено"
 
+#: src/views/preference/OpenAISettings.vue:66
+msgid "Send Include Context"
+msgstr ""
+
 #: src/components/NginxControl/NginxControl.vue:28
 #: src/components/NginxControl/NginxControl.vue:42
 #: src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts:46
@@ -1530,7 +1538,7 @@ msgstr "Отправлено"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:182
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:221
 #: src/views/config/ConfigEdit.vue:41 src/views/domain/DomainList.vue:81
-#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:62
+#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:63
 #: src/views/stream/StreamList.vue:113 src/views/stream/StreamList.vue:81
 #: src/views/system/Upgrade.vue:42
 msgid "Server error"

+ 16 - 8
app/src/language/vi_VN/app.po

@@ -175,7 +175,7 @@ msgstr "Thông tin"
 
 #: src/views/config/ConfigEdit.vue:116
 #: src/views/domain/components/RightSettings.vue:76
-#: src/views/preference/Preference.vue:93
+#: src/views/preference/Preference.vue:94
 #: src/views/stream/components/RightSettings.vue:76
 #, fuzzy
 msgid "Basic"
@@ -653,6 +653,10 @@ msgstr "Bật không thành công"
 msgid "Enable successfully"
 msgstr "Đã bật"
 
+#: src/views/preference/OpenAISettings.vue:67
+msgid "Enable this option will significantly increase the token usage."
+msgstr ""
+
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:175
 msgid "Enable TLS"
 msgstr "Bật TLS"
@@ -973,7 +977,7 @@ msgstr "Đăng nhập thành công"
 msgid "Logout successful"
 msgstr "Đã đăng xuất"
 
-#: src/views/preference/Preference.vue:111
+#: src/views/preference/Preference.vue:112
 msgid "Logrotate"
 msgstr ""
 
@@ -1102,7 +1106,7 @@ msgstr "Đã có phiên bản mới"
 msgid "Next"
 msgstr "Tiếp theo"
 
-#: src/views/preference/Preference.vue:99
+#: src/views/preference/Preference.vue:100
 msgid "Nginx"
 msgstr ""
 
@@ -1230,7 +1234,7 @@ msgstr "Sau khi quá trình xác minh hoàn tất, bản ghi sẽ bị xóa."
 msgid "Online"
 msgstr "Trực tuyến"
 
-#: src/views/preference/Preference.vue:105
+#: src/views/preference/Preference.vue:106
 msgid "OpenAI"
 msgstr ""
 
@@ -1328,7 +1332,7 @@ msgstr ""
 msgid "Pre-release"
 msgstr ""
 
-#: src/routes/index.ts:239 src/views/preference/Preference.vue:88
+#: src/routes/index.ts:239 src/views/preference/Preference.vue:89
 msgid "Preference"
 msgstr "Cài đặt"
 
@@ -1483,7 +1487,7 @@ msgstr "Running"
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/ConfigEdit.vue:97 src/views/domain/DomainEdit.vue:260
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
-#: src/views/preference/Preference.vue:122 src/views/stream/StreamEdit.vue:251
+#: src/views/preference/Preference.vue:123 src/views/stream/StreamEdit.vue:251
 msgid "Save"
 msgstr "Lưu"
 
@@ -1499,7 +1503,7 @@ msgstr "Đã xảy ra lỗi khi lưu %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:45
-#: src/views/preference/Preference.vue:58
+#: src/views/preference/Preference.vue:59
 #, fuzzy
 msgid "Save successfully"
 msgstr "Lưu thành công"
@@ -1524,6 +1528,10 @@ msgstr "Bộ chọn"
 msgid "Send"
 msgstr "Gửi"
 
+#: src/views/preference/OpenAISettings.vue:66
+msgid "Send Include Context"
+msgstr ""
+
 #: src/components/NginxControl/NginxControl.vue:28
 #: src/components/NginxControl/NginxControl.vue:42
 #: src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts:46
@@ -1532,7 +1540,7 @@ msgstr "Gửi"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:182
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:221
 #: src/views/config/ConfigEdit.vue:41 src/views/domain/DomainList.vue:81
-#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:62
+#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:63
 #: src/views/stream/StreamList.vue:113 src/views/stream/StreamList.vue:81
 #: src/views/system/Upgrade.vue:42
 msgid "Server error"

BIN
app/src/language/zh_CN/app.mo


+ 16 - 8
app/src/language/zh_CN/app.po

@@ -168,7 +168,7 @@ msgstr "基本信息"
 
 #: src/views/config/ConfigEdit.vue:116
 #: src/views/domain/components/RightSettings.vue:76
-#: src/views/preference/Preference.vue:93
+#: src/views/preference/Preference.vue:94
 #: src/views/stream/components/RightSettings.vue:76
 msgid "Basic"
 msgstr "基本"
@@ -612,6 +612,10 @@ msgstr "启用失败"
 msgid "Enable successfully"
 msgstr "启用成功"
 
+#: src/views/preference/OpenAISettings.vue:67
+msgid "Enable this option will significantly increase the token usage."
+msgstr "启用该选项将显著增加 token 的使用量。"
+
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:175
 msgid "Enable TLS"
 msgstr "启用 TLS"
@@ -910,7 +914,7 @@ msgstr "登录成功"
 msgid "Logout successful"
 msgstr "登出成功"
 
-#: src/views/preference/Preference.vue:111
+#: src/views/preference/Preference.vue:112
 msgid "Logrotate"
 msgstr "Logrotate"
 
@@ -1036,7 +1040,7 @@ msgstr "新版本发布"
 msgid "Next"
 msgstr "下一步"
 
-#: src/views/preference/Preference.vue:99
+#: src/views/preference/Preference.vue:100
 msgid "Nginx"
 msgstr "Nginx"
 
@@ -1158,7 +1162,7 @@ msgstr "一旦验证完成,这些记录将被删除。"
 msgid "Online"
 msgstr "在线"
 
-#: src/views/preference/Preference.vue:105
+#: src/views/preference/Preference.vue:106
 msgid "OpenAI"
 msgstr "OpenAI"
 
@@ -1253,7 +1257,7 @@ msgstr "请至少选择一个节点!"
 msgid "Pre-release"
 msgstr "预发布"
 
-#: src/routes/index.ts:239 src/views/preference/Preference.vue:88
+#: src/routes/index.ts:239 src/views/preference/Preference.vue:89
 msgid "Preference"
 msgstr "偏好设置"
 
@@ -1394,7 +1398,7 @@ msgstr "运行中"
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/ConfigEdit.vue:97 src/views/domain/DomainEdit.vue:260
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
-#: src/views/preference/Preference.vue:122 src/views/stream/StreamEdit.vue:251
+#: src/views/preference/Preference.vue:123 src/views/stream/StreamEdit.vue:251
 msgid "Save"
 msgstr "保存"
 
@@ -1410,7 +1414,7 @@ msgstr "保存错误 %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:45
-#: src/views/preference/Preference.vue:58
+#: src/views/preference/Preference.vue:59
 msgid "Save successfully"
 msgstr "保存成功"
 
@@ -1434,6 +1438,10 @@ msgstr "选择器"
 msgid "Send"
 msgstr "上传"
 
+#: src/views/preference/OpenAISettings.vue:66
+msgid "Send Include Context"
+msgstr "发送 Include 内容"
+
 #: src/components/NginxControl/NginxControl.vue:28
 #: src/components/NginxControl/NginxControl.vue:42
 #: src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts:46
@@ -1442,7 +1450,7 @@ msgstr "上传"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:182
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:221
 #: src/views/config/ConfigEdit.vue:41 src/views/domain/DomainList.vue:81
-#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:62
+#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:63
 #: src/views/stream/StreamList.vue:113 src/views/stream/StreamList.vue:81
 #: src/views/system/Upgrade.vue:42
 msgid "Server error"

+ 16 - 8
app/src/language/zh_TW/app.po

@@ -176,7 +176,7 @@ msgstr "基本資訊"
 
 #: src/views/config/ConfigEdit.vue:116
 #: src/views/domain/components/RightSettings.vue:76
-#: src/views/preference/Preference.vue:93
+#: src/views/preference/Preference.vue:94
 #: src/views/stream/components/RightSettings.vue:76
 msgid "Basic"
 msgstr "基本"
@@ -635,6 +635,10 @@ msgstr "啟用失敗"
 msgid "Enable successfully"
 msgstr "啟用成功"
 
+#: src/views/preference/OpenAISettings.vue:67
+msgid "Enable this option will significantly increase the token usage."
+msgstr ""
+
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:175
 msgid "Enable TLS"
 msgstr "啟用 TLS"
@@ -948,7 +952,7 @@ msgstr "登入成功"
 msgid "Logout successful"
 msgstr "登出成功"
 
-#: src/views/preference/Preference.vue:111
+#: src/views/preference/Preference.vue:112
 msgid "Logrotate"
 msgstr ""
 
@@ -1074,7 +1078,7 @@ msgstr "新版本發布"
 msgid "Next"
 msgstr "下一步"
 
-#: src/views/preference/Preference.vue:99
+#: src/views/preference/Preference.vue:100
 msgid "Nginx"
 msgstr "Nginx"
 
@@ -1198,7 +1202,7 @@ msgstr ""
 msgid "Online"
 msgstr "線上"
 
-#: src/views/preference/Preference.vue:105
+#: src/views/preference/Preference.vue:106
 msgid "OpenAI"
 msgstr "OpenAI"
 
@@ -1294,7 +1298,7 @@ msgstr "請至少選擇一個節點!"
 msgid "Pre-release"
 msgstr "預先發布"
 
-#: src/routes/index.ts:239 src/views/preference/Preference.vue:88
+#: src/routes/index.ts:239 src/views/preference/Preference.vue:89
 msgid "Preference"
 msgstr "偏好設定"
 
@@ -1446,7 +1450,7 @@ msgstr "執行中"
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/ConfigEdit.vue:97 src/views/domain/DomainEdit.vue:260
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
-#: src/views/preference/Preference.vue:122 src/views/stream/StreamEdit.vue:251
+#: src/views/preference/Preference.vue:123 src/views/stream/StreamEdit.vue:251
 msgid "Save"
 msgstr "儲存"
 
@@ -1462,7 +1466,7 @@ msgstr "儲存錯誤 %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:45
-#: src/views/preference/Preference.vue:58
+#: src/views/preference/Preference.vue:59
 msgid "Save successfully"
 msgstr "儲存成功"
 
@@ -1486,6 +1490,10 @@ msgstr "選擇器"
 msgid "Send"
 msgstr "傳送"
 
+#: src/views/preference/OpenAISettings.vue:66
+msgid "Send Include Context"
+msgstr ""
+
 #: src/components/NginxControl/NginxControl.vue:28
 #: src/components/NginxControl/NginxControl.vue:42
 #: src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts:46
@@ -1494,7 +1502,7 @@ msgstr "傳送"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:182
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:221
 #: src/views/config/ConfigEdit.vue:41 src/views/domain/DomainList.vue:81
-#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:62
+#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:63
 #: src/views/stream/StreamList.vue:113 src/views/stream/StreamList.vue:81
 #: src/views/system/Upgrade.vue:42
 msgid "Server error"

+ 1 - 0
app/src/views/config/Config.vue

@@ -50,6 +50,7 @@ watch(route, () => {
       :columns="configColumns"
       disable-delete
       disable_search
+      disabled-view
       row-key="name"
       :get_params="get_params"
       @click-edit="(r, row) => {

+ 5 - 6
app/src/views/config/ConfigEdit.vue

@@ -4,7 +4,6 @@ import { message } from 'ant-design-vue'
 import type { Ref } from 'vue'
 import { formatDateTime } from '@/lib/helper'
 import FooterToolBar from '@/components/FooterToolbar/FooterToolBar.vue'
-
 import config from '@/api/config'
 import CodeEditor from '@/components/CodeEditor/CodeEditor.vue'
 import ngx from '@/api/ngx'
@@ -26,7 +25,7 @@ const name = computed(() => {
 
 const configText = ref('')
 const history_chatgpt_record = ref([]) as Ref<ChatComplicationMessage[]>
-const file_path = ref('')
+const filepath = ref('')
 const active_key = ref(['1', '2'])
 const modified_at = ref('')
 
@@ -35,7 +34,7 @@ function init() {
     config.get(name.value).then(r => {
       configText.value = r.content
       history_chatgpt_record.value = r.chatgpt_messages
-      file_path.value = r.file_path
+      filepath.value = r.filepath
       modified_at.value = r.modified_at
     }).catch(r => {
       message.error(r.message ?? $gettext('Server error'))
@@ -44,7 +43,7 @@ function init() {
   else {
     configText.value = ''
     history_chatgpt_record.value = []
-    file_path.value = ''
+    filepath.value = ''
   }
 }
 
@@ -117,7 +116,7 @@ function format_code() {
           >
             <AForm layout="vertical">
               <AFormItem :label="$gettext('Path')">
-                {{ file_path }}
+                {{ filepath }}
               </AFormItem>
               <AFormItem :label="$gettext('Updated at')">
                 {{ formatDateTime(modified_at) }}
@@ -131,7 +130,7 @@ function format_code() {
             <ChatGPT
               v-model:history-messages="history_chatgpt_record"
               :content="configText"
-              :path="file_path"
+              :path="filepath"
             />
           </ACollapsePanel>
         </ACollapse>

+ 3 - 0
app/src/views/domain/DomainEdit.vue

@@ -33,6 +33,7 @@ const cert_info_map: Record<string, CertificateInfo> = reactive({})
 
 const auto_cert = ref(false)
 const enabled = ref(false)
+const filepath = ref('')
 const configText = ref('')
 const advance_mode_ref = ref(false)
 const saving = ref(false)
@@ -67,6 +68,7 @@ function handle_response(r: Site) {
   parse_error_status.value = false
   parse_error_message.value = ''
   filename.value = r.name
+  filepath.value = r.filepath
   configText.value = r.config
   enabled.value = r.enabled
   auto_cert.value = r.auto_cert
@@ -156,6 +158,7 @@ provide('history_chatgpt_record', history_chatgpt_record)
 provide('enabled', enabled)
 provide('name', name)
 provide('filename', filename)
+provide('filepath', filepath)
 provide('data', data)
 </script>
 

+ 2 - 0
app/src/views/domain/DomainList.vue

@@ -108,11 +108,13 @@ watch(route, () => {
       :columns="columns"
       row-key="name"
       disable-delete
+      disabled-view
       @click-edit="r => $router.push({
         path: `/domain/${r}`,
       })"
     >
       <template #actions="{ record }">
+        <ADivider type="vertical" />
         <AButton
           v-if="record.enabled"
           type="link"

+ 2 - 3
app/src/views/domain/components/RightSettings.vue

@@ -8,15 +8,14 @@ import { formatDateTime } from '@/lib/helper'
 import Deploy from '@/views/domain/components/Deploy.vue'
 import { useSettingsStore } from '@/pinia'
 import type { ChatComplicationMessage } from '@/api/openai'
-import type { NgxConfig } from '@/api/ngx'
 import type { CheckedType } from '@/types'
 
 const settings = useSettingsStore()
 
 const configText = inject('configText') as Ref<string>
-const ngx_config = inject('ngx_config') as Ref<NgxConfig>
 const enabled = inject('enabled') as Ref<boolean>
 const name = inject('name') as Ref<string>
+const filepath = inject('filepath') as Ref<string>
 const history_chatgpt_record = inject('history_chatgpt_record') as Ref<ChatComplicationMessage[]>
 const filename = inject('filename') as Ref<string | number | undefined>
 const data = inject('data') as Ref<Site>
@@ -102,7 +101,7 @@ function on_change_enabled(checked: CheckedType) {
         <ChatGPT
           v-model:history-messages="history_chatgpt_record"
           :content="configText"
-          :path="ngx_config.file_name"
+          :path="filepath"
         />
       </ACollapsePanel>
     </ACollapse>

+ 3 - 0
app/src/views/stream/StreamEdit.vue

@@ -34,6 +34,7 @@ const configText = ref('')
 const advance_mode_ref = ref(false)
 const saving = ref(false)
 const filename = ref('')
+const filepath = ref('')
 const parse_error_status = ref(false)
 const parse_error_message = ref('')
 const data = ref({})
@@ -61,6 +62,7 @@ function handle_response(r: Stream) {
   parse_error_status.value = false
   parse_error_message.value = ''
   filename.value = r.name
+  filepath.value = r.filepath
   configText.value = r.config
   enabled.value = r.enabled
   history_chatgpt_record.value = r.chatgpt_messages
@@ -148,6 +150,7 @@ provide('history_chatgpt_record', history_chatgpt_record)
 provide('enabled', enabled)
 provide('name', name)
 provide('filename', filename)
+provide('filepath', filepath)
 provide('data', data)
 </script>
 

+ 2 - 0
app/src/views/stream/StreamList.vue

@@ -129,11 +129,13 @@ function handleAddStream() {
       :columns="columns"
       row-key="name"
       disable-delete
+      disabled-view
       @click-edit="r => $router.push({
         path: `/stream/${r}`,
       })"
     >
       <template #actions="{ record }">
+        <ADivider type="vertical" />
         <AButton
           v-if="record.enabled"
           type="link"

+ 3 - 5
app/src/views/stream/components/RightSettings.vue

@@ -8,17 +8,16 @@ import { formatDateTime } from '@/lib/helper'
 import Deploy from '@/views/stream/components/Deploy.vue'
 import { useSettingsStore } from '@/pinia'
 import type { ChatComplicationMessage } from '@/api/openai'
-import type { NgxConfig } from '@/api/ngx'
 import type { CheckedType } from '@/types'
 
 const settings = useSettingsStore()
 
 const configText = inject('configText') as Ref<string>
-const ngx_config = inject('ngx_config') as Ref<NgxConfig>
 const enabled = inject('enabled') as Ref<boolean>
 const name = inject('name') as Ref<string>
 const history_chatgpt_record = inject('history_chatgpt_record') as Ref<ChatComplicationMessage[]>
-const filename = inject('filename') as Ref<string | number | undefined>
+const filename = inject('filename') as Ref<string>
+const filepath = inject('filepath') as Ref<string>
 const data = inject('data') as Ref<Stream>
 
 const [modal, ContextHolder] = Modal.useModal()
@@ -58,7 +57,6 @@ function on_change_enabled(checked: CheckedType) {
     },
   })
 }
-
 </script>
 
 <template>
@@ -102,7 +100,7 @@ function on_change_enabled(checked: CheckedType) {
         <ChatGPT
           v-model:history-messages="history_chatgpt_record"
           :content="configText"
-          :path="ngx_config.file_name"
+          :path="filepath"
         />
       </ACollapsePanel>
     </ACollapse>

+ 102 - 0
internal/chatbot/context.go

@@ -0,0 +1,102 @@
+package chatbot
+
+import (
+	"github.com/0xJacky/Nginx-UI/internal/helper"
+	"github.com/0xJacky/Nginx-UI/internal/logger"
+	"github.com/0xJacky/Nginx-UI/internal/nginx"
+	"github.com/sashabaranov/go-openai"
+	"os"
+	"regexp"
+	"strings"
+)
+
+type includeContext struct {
+	Paths    []string
+	PathsMap map[string]bool
+}
+
+func IncludeContext(filename string) (includes []string) {
+	c := &includeContext{
+		Paths:    make([]string, 0),
+		PathsMap: make(map[string]bool),
+	}
+
+	c.extractIncludes(filename)
+
+	return c.Paths
+}
+
+// extractIncludes extracts all include statements from the given nginx configuration file.
+func (c *includeContext) extractIncludes(filename string) {
+	if !helper.FileExists(filename) {
+		logger.Error("File does not exist: ", filename)
+		return
+	}
+
+	// Read the file content
+	content, err := os.ReadFile(filename)
+	if err != nil {
+		logger.Error(err)
+		return
+	}
+
+	// Find all include statements
+	pattern := regexp.MustCompile(`(?m)^\s*include\s+([^;]+);`)
+	matches := pattern.FindAllStringSubmatch(string(content), -1)
+	for _, match := range matches {
+		if len(match) > 1 {
+			// Resolve the path of the included file
+			includePath := match[1]
+
+			// to avoid infinite loop
+			if c.PathsMap[includePath] {
+				continue
+			}
+
+			c.push(includePath)
+
+			// Recursively extract includes from the included file
+			c.extractIncludes(includePath)
+		}
+	}
+
+	return
+}
+
+func (c *includeContext) push(path string) {
+	c.Paths = append(c.Paths, path)
+	c.PathsMap[path] = true
+}
+
+// getConfigIncludeContext returns the context of the given filename.
+func getConfigIncludeContext(filename string) (multiContent []openai.ChatMessagePart) {
+	multiContent = make([]openai.ChatMessagePart, 0)
+
+	if !helper.IsUnderDirectory(filename, nginx.GetConfPath()) {
+		return
+	}
+
+	includes := IncludeContext(filename)
+	logger.Debug(includes)
+	var sb strings.Builder
+	for _, include := range includes {
+		text, _ := os.ReadFile(nginx.GetConfPath(include))
+
+		if len(text) == 0 {
+			continue
+		}
+
+		sb.WriteString("The Content of ")
+		sb.WriteString(include)
+		sb.WriteString(",")
+		sb.WriteString(string(text))
+
+		multiContent = append(multiContent, openai.ChatMessagePart{
+			Type: openai.ChatMessagePartTypeText,
+			Text: sb.String(),
+		})
+
+		sb.Reset()
+	}
+	return
+}

+ 23 - 0
internal/chatbot/context_test.go

@@ -0,0 +1,23 @@
+package chatbot
+
+import (
+	"github.com/stretchr/testify/assert"
+	"regexp"
+	"testing"
+)
+
+func TestRegex(t *testing.T) {
+	content := `
+server {
+    listen 80;
+    listen [::]:80;
+    server_name _;
+    include error_json;
+}
+`
+	pattern := regexp.MustCompile(`(?m)^\s*include\s+([^;]+);`)
+	matches := pattern.FindAllStringSubmatch(content, -1)
+
+	assert.Equal(t, 1, len(matches))
+	assert.Equal(t, "error_json", matches[0][1])
+}

+ 22 - 0
internal/chatbot/messages.go

@@ -0,0 +1,22 @@
+package chatbot
+
+import (
+    "github.com/sashabaranov/go-openai"
+)
+
+func ChatCompletionWithContext(filename string, messages []openai.ChatCompletionMessage) []openai.ChatCompletionMessage {
+
+    for i := len(messages) - 1; i >= 0; i-- {
+        if messages[i].Role == openai.ChatMessageRoleUser {
+            // openai.ChatCompletionMessage: can't use both Content and MultiContent properties simultaneously
+            multiContent := getConfigIncludeContext(filename)
+            multiContent = append(multiContent, openai.ChatMessagePart{
+                Type: openai.ChatMessagePartTypeText,
+                Text: messages[i].Content,
+            })
+            messages[i].Content = ""
+            messages[i].MultiContent = multiContent
+        }
+    }
+    return messages
+}

+ 26 - 0
internal/chatbot/messages_test.go

@@ -0,0 +1,26 @@
+package chatbot
+
+import (
+	"github.com/sashabaranov/go-openai"
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+func TestChatCompletionWithContext(t *testing.T) {
+	filename := "test"
+	messages := []openai.ChatCompletionMessage{
+		{
+			Role: openai.ChatMessageRoleSystem,
+		},
+		{
+			Role: openai.ChatMessageRoleUser,
+		},
+		{
+			Role: openai.ChatMessageRoleAssistant,
+		},
+	}
+
+	messages = ChatCompletionWithContext(filename, messages)
+
+	assert.NotNil(t, messages[1].MultiContent)
+}

+ 1 - 1
internal/config/config.go

@@ -9,7 +9,7 @@ type Config struct {
 	Name            string                         `json:"name"`
 	Content         string                         `json:"content,omitempty"`
 	ChatGPTMessages []openai.ChatCompletionMessage `json:"chatgpt_messages,omitempty"`
-	FilePath        string                         `json:"file_path,omitempty"`
+	FilePath        string                         `json:"filepath,omitempty"`
 	ModifiedAt      time.Time                      `json:"modified_at"`
 	Size            int64                          `json:"size,omitempty"`
 	IsDir           bool                           `json:"is_dir"`

+ 25 - 0
internal/helper/directory.go

@@ -0,0 +1,25 @@
+package helper
+
+import (
+	"github.com/0xJacky/Nginx-UI/internal/logger"
+	"path/filepath"
+	"strings"
+)
+
+func IsUnderDirectory(path, directory string) bool {
+	absPath, err := filepath.Abs(path)
+	if err != nil {
+		logger.Error(err)
+		return false
+	}
+
+	absDirectory, err := filepath.Abs(directory)
+	if err != nil {
+		logger.Error(err)
+		return false
+	}
+
+	absDirectory = filepath.Clean(absDirectory) + string(filepath.Separator)
+
+	return strings.HasPrefix(absPath, absDirectory)
+}

+ 11 - 0
internal/helper/directory_test.go

@@ -0,0 +1,11 @@
+package helper
+
+import (
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+func TestIsUnderDirectory(t *testing.T) {
+	assert.Equal(t, true, IsUnderDirectory("/etc/nginx/nginx.conf", "/etc/nginx"))
+	assert.Equal(t, false, IsUnderDirectory("../../root/nginx.conf", "/etc/nginx"))
+}

+ 2 - 2
model/config_backup.go

@@ -10,7 +10,7 @@ type ConfigBackup struct {
 	Model
 
 	Name     string `json:"name"`
-	FilePath string `json:"file_path"`
+	FilePath string `json:"filepath"`
 	Content  string `json:"content" gorm:"type:text"`
 }
 
@@ -18,7 +18,7 @@ type ConfigBackupListItem struct {
 	Model
 
 	Name     string `json:"name"`
-	FilePath string `json:"file_path"`
+	FilePath string `json:"filepath"`
 }
 
 func GetBackupList(path string) (configs []ConfigBackupListItem) {

+ 3 - 3
query/config_backups.gen.go

@@ -33,7 +33,7 @@ func newConfigBackup(db *gorm.DB, opts ...gen.DOOption) configBackup {
 	_configBackup.UpdatedAt = field.NewTime(tableName, "updated_at")
 	_configBackup.DeletedAt = field.NewField(tableName, "deleted_at")
 	_configBackup.Name = field.NewString(tableName, "name")
-	_configBackup.FilePath = field.NewString(tableName, "file_path")
+	_configBackup.FilePath = field.NewString(tableName, "filepath")
 	_configBackup.Content = field.NewString(tableName, "content")
 
 	_configBackup.fillFieldMap()
@@ -73,7 +73,7 @@ func (c *configBackup) updateTableName(table string) *configBackup {
 	c.UpdatedAt = field.NewTime(table, "updated_at")
 	c.DeletedAt = field.NewField(table, "deleted_at")
 	c.Name = field.NewString(table, "name")
-	c.FilePath = field.NewString(table, "file_path")
+	c.FilePath = field.NewString(table, "filepath")
 	c.Content = field.NewString(table, "content")
 
 	c.fillFieldMap()
@@ -97,7 +97,7 @@ func (c *configBackup) fillFieldMap() {
 	c.fieldMap["updated_at"] = c.UpdatedAt
 	c.fieldMap["deleted_at"] = c.DeletedAt
 	c.fieldMap["name"] = c.Name
-	c.fieldMap["file_path"] = c.FilePath
+	c.fieldMap["filepath"] = c.FilePath
 	c.fieldMap["content"] = c.Content
 }