openai.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. package openai
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/0xJacky/Nginx-UI/api"
  6. "github.com/0xJacky/Nginx-UI/model"
  7. "github.com/0xJacky/Nginx-UI/query"
  8. "github.com/0xJacky/Nginx-UI/settings"
  9. "github.com/gin-gonic/gin"
  10. "github.com/pkg/errors"
  11. "github.com/sashabaranov/go-openai"
  12. "io"
  13. "net/http"
  14. "net/url"
  15. "os"
  16. )
  17. 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."
  18. func MakeChatCompletionRequest(c *gin.Context) {
  19. var json struct {
  20. Messages []openai.ChatCompletionMessage `json:"messages"`
  21. }
  22. if !api.BindAndValid(c, &json) {
  23. return
  24. }
  25. messages := []openai.ChatCompletionMessage{
  26. {
  27. Role: openai.ChatMessageRoleSystem,
  28. Content: ChatGPTInitPrompt,
  29. },
  30. }
  31. messages = append(messages, json.Messages...)
  32. // sse server
  33. c.Writer.Header().Set("Content-Type", "text/event-stream")
  34. c.Writer.Header().Set("Cache-Control", "no-cache")
  35. c.Writer.Header().Set("Connection", "keep-alive")
  36. c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
  37. if settings.OpenAISettings.Token == "" {
  38. c.Stream(func(w io.Writer) bool {
  39. c.SSEvent("message", gin.H{
  40. "type": "error",
  41. "content": "[Error] OpenAI token is empty",
  42. })
  43. return false
  44. })
  45. return
  46. }
  47. config := openai.DefaultConfig(settings.OpenAISettings.Token)
  48. if settings.OpenAISettings.Proxy != "" {
  49. proxyUrl, err := url.Parse(settings.OpenAISettings.Proxy)
  50. if err != nil {
  51. c.Stream(func(w io.Writer) bool {
  52. c.SSEvent("message", gin.H{
  53. "type": "error",
  54. "content": err.Error(),
  55. })
  56. return false
  57. })
  58. return
  59. }
  60. transport := &http.Transport{
  61. Proxy: http.ProxyURL(proxyUrl),
  62. }
  63. config.HTTPClient = &http.Client{
  64. Transport: transport,
  65. }
  66. }
  67. if settings.OpenAISettings.BaseUrl != "" {
  68. config.BaseURL = settings.OpenAISettings.BaseUrl
  69. }
  70. openaiClient := openai.NewClientWithConfig(config)
  71. ctx := context.Background()
  72. req := openai.ChatCompletionRequest{
  73. Model: settings.OpenAISettings.Model,
  74. Messages: messages,
  75. Stream: true,
  76. }
  77. stream, err := openaiClient.CreateChatCompletionStream(ctx, req)
  78. if err != nil {
  79. fmt.Printf("CompletionStream error: %v\n", err)
  80. c.Stream(func(w io.Writer) bool {
  81. c.SSEvent("message", gin.H{
  82. "type": "error",
  83. "content": err.Error(),
  84. })
  85. return false
  86. })
  87. return
  88. }
  89. defer stream.Close()
  90. msgChan := make(chan string)
  91. go func() {
  92. for {
  93. response, err := stream.Recv()
  94. if errors.Is(err, io.EOF) {
  95. close(msgChan)
  96. fmt.Println()
  97. return
  98. }
  99. if err != nil {
  100. fmt.Printf("Stream error: %v\n", err)
  101. close(msgChan)
  102. return
  103. }
  104. message := fmt.Sprintf("%s", response.Choices[0].Delta.Content)
  105. fmt.Printf("%s", message)
  106. _ = os.Stdout.Sync()
  107. msgChan <- message
  108. }
  109. }()
  110. c.Stream(func(w io.Writer) bool {
  111. if m, ok := <-msgChan; ok {
  112. c.SSEvent("message", gin.H{
  113. "type": "message",
  114. "content": m,
  115. })
  116. return true
  117. }
  118. return false
  119. })
  120. }
  121. func StoreChatGPTRecord(c *gin.Context) {
  122. var json struct {
  123. FileName string `json:"file_name"`
  124. Messages []openai.ChatCompletionMessage `json:"messages"`
  125. }
  126. if !api.BindAndValid(c, &json) {
  127. return
  128. }
  129. name := json.FileName
  130. g := query.ChatGPTLog
  131. _, err := g.Where(g.Name.Eq(name)).FirstOrCreate()
  132. if err != nil {
  133. api.ErrHandler(c, err)
  134. return
  135. }
  136. _, err = g.Where(g.Name.Eq(name)).Updates(&model.ChatGPTLog{
  137. Name: name,
  138. Content: json.Messages,
  139. })
  140. if err != nil {
  141. api.ErrHandler(c, err)
  142. return
  143. }
  144. c.JSON(http.StatusOK, gin.H{
  145. "message": "ok",
  146. })
  147. }