1
0

openai.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. package openai
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/0xJacky/Nginx-UI/api"
  6. "github.com/0xJacky/Nginx-UI/internal/chatbot"
  7. "github.com/0xJacky/Nginx-UI/internal/transport"
  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. )
  15. const ChatGPTInitPrompt = `You are a assistant who can help users write and optimise the configurations of Nginx,
  16. the first user message contains the content of the configuration file which is currently opened by the user and
  17. the current language code(CLC). You suppose to use the language corresponding to the CLC to give the first reply.
  18. Later the language environment depends on the user message.
  19. The first reply should involve the key information of the file and ask user what can you help them.`
  20. func MakeChatCompletionRequest(c *gin.Context) {
  21. var json struct {
  22. Filepath string `json:"filepath"`
  23. Messages []openai.ChatCompletionMessage `json:"messages"`
  24. }
  25. if !api.BindAndValid(c, &json) {
  26. return
  27. }
  28. messages := []openai.ChatCompletionMessage{
  29. {
  30. Role: openai.ChatMessageRoleSystem,
  31. Content: ChatGPTInitPrompt,
  32. },
  33. }
  34. messages = append(messages, json.Messages...)
  35. if json.Filepath != "" {
  36. messages = chatbot.ChatCompletionWithContext(json.Filepath, messages)
  37. }
  38. // SSE server
  39. c.Writer.Header().Set("Content-Type", "text/event-stream; charset=utf-8")
  40. c.Writer.Header().Set("Cache-Control", "no-cache")
  41. c.Writer.Header().Set("Connection", "keep-alive")
  42. c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
  43. config := openai.DefaultConfig(settings.OpenAISettings.Token)
  44. if settings.OpenAISettings.Proxy != "" {
  45. t, err := transport.NewTransport(transport.WithProxy(settings.OpenAISettings.Proxy))
  46. if err != nil {
  47. c.Stream(func(w io.Writer) bool {
  48. c.SSEvent("message", gin.H{
  49. "type": "error",
  50. "content": err.Error(),
  51. })
  52. return false
  53. })
  54. return
  55. }
  56. config.HTTPClient = &http.Client{
  57. Transport: t,
  58. }
  59. }
  60. if settings.OpenAISettings.BaseUrl != "" {
  61. config.BaseURL = settings.OpenAISettings.BaseUrl
  62. }
  63. openaiClient := openai.NewClientWithConfig(config)
  64. ctx := context.Background()
  65. req := openai.ChatCompletionRequest{
  66. Model: settings.OpenAISettings.Model,
  67. Messages: messages,
  68. Stream: true,
  69. }
  70. stream, err := openaiClient.CreateChatCompletionStream(ctx, req)
  71. if err != nil {
  72. fmt.Printf("CompletionStream error: %v\n", err)
  73. c.Stream(func(w io.Writer) bool {
  74. c.SSEvent("message", gin.H{
  75. "type": "error",
  76. "content": err.Error(),
  77. })
  78. return false
  79. })
  80. return
  81. }
  82. defer stream.Close()
  83. msgChan := make(chan string)
  84. go func() {
  85. defer close(msgChan)
  86. for {
  87. response, err := stream.Recv()
  88. if errors.Is(err, io.EOF) {
  89. fmt.Println()
  90. return
  91. }
  92. if err != nil {
  93. fmt.Printf("Stream error: %v\n", err)
  94. return
  95. }
  96. message := fmt.Sprintf("%s", response.Choices[0].Delta.Content)
  97. msgChan <- message
  98. }
  99. }()
  100. c.Stream(func(w io.Writer) bool {
  101. if m, ok := <-msgChan; ok {
  102. c.SSEvent("message", gin.H{
  103. "type": "message",
  104. "content": m,
  105. })
  106. return true
  107. }
  108. return false
  109. })
  110. }