mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-11 02:15:48 +02:00
feat(chat): support other local llm #331
This commit is contained in:
parent
08631437ee
commit
3b116b3654
4 changed files with 356 additions and 361 deletions
|
@ -1,305 +1,305 @@
|
|||
package nginx
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/0xJacky/Nginx-UI/api"
|
||||
"github.com/0xJacky/Nginx-UI/internal/logger"
|
||||
"github.com/0xJacky/Nginx-UI/internal/nginx"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/hpcloud/tail"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cast"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"encoding/json"
|
||||
"github.com/0xJacky/Nginx-UI/api"
|
||||
"github.com/0xJacky/Nginx-UI/internal/logger"
|
||||
"github.com/0xJacky/Nginx-UI/internal/nginx"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/hpcloud/tail"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cast"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
PageSize = 128 * 1024
|
||||
PageSize = 128 * 1024
|
||||
)
|
||||
|
||||
type controlStruct struct {
|
||||
Type string `json:"type"`
|
||||
ConfName string `json:"conf_name"`
|
||||
ServerIdx int `json:"server_idx"`
|
||||
DirectiveIdx int `json:"directive_idx"`
|
||||
Type string `json:"type"`
|
||||
ConfName string `json:"conf_name"`
|
||||
ServerIdx int `json:"server_idx"`
|
||||
DirectiveIdx int `json:"directive_idx"`
|
||||
}
|
||||
|
||||
type nginxLogPageResp struct {
|
||||
Content string `json:"content"`
|
||||
Page int64 `json:"page"`
|
||||
Content string `json:"content"`
|
||||
Page int64 `json:"page"`
|
||||
}
|
||||
|
||||
func GetNginxLogPage(c *gin.Context) {
|
||||
page := cast.ToInt64(c.Query("page"))
|
||||
if page < 0 {
|
||||
page = 0
|
||||
}
|
||||
page := cast.ToInt64(c.Query("page"))
|
||||
if page < 0 {
|
||||
page = 0
|
||||
}
|
||||
|
||||
var control controlStruct
|
||||
if !api.BindAndValid(c, &control) {
|
||||
return
|
||||
}
|
||||
var control controlStruct
|
||||
if !api.BindAndValid(c, &control) {
|
||||
return
|
||||
}
|
||||
|
||||
logPath, err := getLogPath(&control)
|
||||
logPath, err := getLogPath(&control)
|
||||
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
logFileStat, err := os.Stat(logPath)
|
||||
logFileStat, err := os.Stat(logPath)
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, nginxLogPageResp{})
|
||||
logger.Error(err)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, nginxLogPageResp{})
|
||||
logger.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
if !logFileStat.Mode().IsRegular() {
|
||||
c.JSON(http.StatusOK, nginxLogPageResp{})
|
||||
logger.Error("log file is not regular file:", logPath)
|
||||
return
|
||||
}
|
||||
if !logFileStat.Mode().IsRegular() {
|
||||
c.JSON(http.StatusOK, nginxLogPageResp{})
|
||||
logger.Error("log file is not regular file:", logPath)
|
||||
return
|
||||
}
|
||||
|
||||
f, err := os.Open(logPath)
|
||||
f, err := os.Open(logPath)
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, nginxLogPageResp{})
|
||||
logger.Error(err)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, nginxLogPageResp{})
|
||||
logger.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
totalPage := logFileStat.Size() / PageSize
|
||||
totalPage := logFileStat.Size() / PageSize
|
||||
|
||||
if logFileStat.Size()%PageSize > 0 {
|
||||
totalPage++
|
||||
}
|
||||
if logFileStat.Size()%PageSize > 0 {
|
||||
totalPage++
|
||||
}
|
||||
|
||||
var buf []byte
|
||||
var offset int64
|
||||
if page == 0 {
|
||||
page = totalPage
|
||||
}
|
||||
var buf []byte
|
||||
var offset int64
|
||||
if page == 0 {
|
||||
page = totalPage
|
||||
}
|
||||
|
||||
buf = make([]byte, PageSize)
|
||||
offset = (page - 1) * PageSize
|
||||
buf = make([]byte, PageSize)
|
||||
offset = (page - 1) * PageSize
|
||||
|
||||
// seek
|
||||
_, err = f.Seek(offset, io.SeekStart)
|
||||
if err != nil && err != io.EOF {
|
||||
c.JSON(http.StatusOK, nginxLogPageResp{})
|
||||
logger.Error(err)
|
||||
return
|
||||
}
|
||||
// seek
|
||||
_, err = f.Seek(offset, io.SeekStart)
|
||||
if err != nil && err != io.EOF {
|
||||
c.JSON(http.StatusOK, nginxLogPageResp{})
|
||||
logger.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
n, err := f.Read(buf)
|
||||
n, err := f.Read(buf)
|
||||
|
||||
if err != nil && err != io.EOF {
|
||||
c.JSON(http.StatusOK, nginxLogPageResp{})
|
||||
logger.Error(err)
|
||||
return
|
||||
}
|
||||
if err != nil && err != io.EOF {
|
||||
c.JSON(http.StatusOK, nginxLogPageResp{})
|
||||
logger.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, nginxLogPageResp{
|
||||
Page: page,
|
||||
Content: string(buf[:n]),
|
||||
})
|
||||
c.JSON(http.StatusOK, nginxLogPageResp{
|
||||
Page: page,
|
||||
Content: string(buf[:n]),
|
||||
})
|
||||
}
|
||||
|
||||
func getLogPath(control *controlStruct) (logPath string, err error) {
|
||||
switch control.Type {
|
||||
case "site":
|
||||
var config *nginx.NgxConfig
|
||||
path := nginx.GetConfPath("sites-available", control.ConfName)
|
||||
config, err = nginx.ParseNgxConfig(path)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "error parsing ngx config")
|
||||
return
|
||||
}
|
||||
switch control.Type {
|
||||
case "site":
|
||||
var config *nginx.NgxConfig
|
||||
path := nginx.GetConfPath("sites-available", control.ConfName)
|
||||
config, err = nginx.ParseNgxConfig(path)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "error parsing ngx config")
|
||||
return
|
||||
}
|
||||
|
||||
if control.ServerIdx >= len(config.Servers) {
|
||||
err = errors.New("serverIdx out of range")
|
||||
return
|
||||
}
|
||||
if control.ServerIdx >= len(config.Servers) {
|
||||
err = errors.New("serverIdx out of range")
|
||||
return
|
||||
}
|
||||
|
||||
if control.DirectiveIdx >= len(config.Servers[control.ServerIdx].Directives) {
|
||||
err = errors.New("DirectiveIdx out of range")
|
||||
return
|
||||
}
|
||||
if control.DirectiveIdx >= len(config.Servers[control.ServerIdx].Directives) {
|
||||
err = errors.New("DirectiveIdx out of range")
|
||||
return
|
||||
}
|
||||
|
||||
directive := config.Servers[control.ServerIdx].Directives[control.DirectiveIdx]
|
||||
switch directive.Directive {
|
||||
case "access_log", "error_log":
|
||||
// ok
|
||||
default:
|
||||
err = errors.New("directive.Params neither access_log nor error_log")
|
||||
return
|
||||
}
|
||||
directive := config.Servers[control.ServerIdx].Directives[control.DirectiveIdx]
|
||||
switch directive.Directive {
|
||||
case "access_log", "error_log":
|
||||
// ok
|
||||
default:
|
||||
err = errors.New("directive.Params neither access_log nor error_log")
|
||||
return
|
||||
}
|
||||
|
||||
if directive.Params == "" {
|
||||
err = errors.New("directive.Params is empty")
|
||||
return
|
||||
}
|
||||
if directive.Params == "" {
|
||||
err = errors.New("directive.Params is empty")
|
||||
return
|
||||
}
|
||||
|
||||
// fix: access_log /var/log/test.log main;
|
||||
p := strings.Split(directive.Params, " ")
|
||||
if len(p) > 0 {
|
||||
logPath = p[0]
|
||||
}
|
||||
// fix: access_log /var/log/test.log main;
|
||||
p := strings.Split(directive.Params, " ")
|
||||
if len(p) > 0 {
|
||||
logPath = p[0]
|
||||
}
|
||||
|
||||
case "error":
|
||||
path := nginx.GetErrorLogPath()
|
||||
case "error":
|
||||
path := nginx.GetErrorLogPath()
|
||||
|
||||
if path == "" {
|
||||
err = errors.New("settings.NginxLogSettings.ErrorLogPath is empty," +
|
||||
" refer to https://nginxui.com/guide/config-nginx.html for more information")
|
||||
return
|
||||
}
|
||||
if path == "" {
|
||||
err = errors.New("settings.NginxLogSettings.ErrorLogPath is empty," +
|
||||
" refer to https://nginxui.com/guide/config-nginx.html for more information")
|
||||
return
|
||||
}
|
||||
|
||||
logPath = path
|
||||
default:
|
||||
path := nginx.GetAccessLogPath()
|
||||
logPath = path
|
||||
default:
|
||||
path := nginx.GetAccessLogPath()
|
||||
|
||||
if path == "" {
|
||||
err = errors.New("settings.NginxLogSettings.AccessLogPath is empty," +
|
||||
" refer to https://nginxui.com/guide/config-nginx.html for more information")
|
||||
return
|
||||
}
|
||||
if path == "" {
|
||||
err = errors.New("settings.NginxLogSettings.AccessLogPath is empty," +
|
||||
" refer to https://nginxui.com/guide/config-nginx.html for more information")
|
||||
return
|
||||
}
|
||||
|
||||
logPath = path
|
||||
}
|
||||
logPath = path
|
||||
}
|
||||
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
func tailNginxLog(ws *websocket.Conn, controlChan chan controlStruct, errChan chan error) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
logger.Error(err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
logger.Error(err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
control := <-controlChan
|
||||
control := <-controlChan
|
||||
|
||||
for {
|
||||
logPath, err := getLogPath(&control)
|
||||
for {
|
||||
logPath, err := getLogPath(&control)
|
||||
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
|
||||
seek := tail.SeekInfo{
|
||||
Offset: 0,
|
||||
Whence: io.SeekEnd,
|
||||
}
|
||||
seek := tail.SeekInfo{
|
||||
Offset: 0,
|
||||
Whence: io.SeekEnd,
|
||||
}
|
||||
|
||||
stat, err := os.Stat(logPath)
|
||||
if os.IsNotExist(err) {
|
||||
errChan <- errors.New("[error] log path not exists " + logPath)
|
||||
return
|
||||
}
|
||||
stat, err := os.Stat(logPath)
|
||||
if os.IsNotExist(err) {
|
||||
errChan <- errors.New("[error] log path not exists " + logPath)
|
||||
return
|
||||
}
|
||||
|
||||
if !stat.Mode().IsRegular() {
|
||||
errChan <- errors.New("[error] " + logPath + " is not a regular file. " +
|
||||
"If you are using nginx-ui in docker container, please refer to " +
|
||||
"https://nginxui.com/zh_CN/guide/config-nginx-log.html for more information.")
|
||||
return
|
||||
}
|
||||
if !stat.Mode().IsRegular() {
|
||||
errChan <- errors.New("[error] " + logPath + " is not a regular file. " +
|
||||
"If you are using nginx-ui in docker container, please refer to " +
|
||||
"https://nginxui.com/zh_CN/guide/config-nginx-log.html for more information.")
|
||||
return
|
||||
}
|
||||
|
||||
// Create a tail
|
||||
t, err := tail.TailFile(logPath, tail.Config{Follow: true,
|
||||
ReOpen: true, Location: &seek})
|
||||
// Create a tail
|
||||
t, err := tail.TailFile(logPath, tail.Config{Follow: true,
|
||||
ReOpen: true, Location: &seek})
|
||||
|
||||
if err != nil {
|
||||
errChan <- errors.Wrap(err, "error tailing log")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
errChan <- errors.Wrap(err, "error tailing log")
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
var next = false
|
||||
select {
|
||||
case line := <-t.Lines:
|
||||
// Print the text of each received line
|
||||
if line == nil {
|
||||
continue
|
||||
}
|
||||
for {
|
||||
var next = false
|
||||
select {
|
||||
case line := <-t.Lines:
|
||||
// Print the text of each received line
|
||||
if line == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
err = ws.WriteMessage(websocket.TextMessage, []byte(line.Text))
|
||||
err = ws.WriteMessage(websocket.TextMessage, []byte(line.Text))
|
||||
|
||||
if err != nil && websocket.IsUnexpectedCloseError(err, websocket.CloseNormalClosure) {
|
||||
errChan <- errors.Wrap(err, "error tailNginxLog write message")
|
||||
return
|
||||
}
|
||||
case control = <-controlChan:
|
||||
next = true
|
||||
break
|
||||
}
|
||||
if next {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil && websocket.IsUnexpectedCloseError(err, websocket.CloseNormalClosure) {
|
||||
errChan <- errors.Wrap(err, "error tailNginxLog write message")
|
||||
return
|
||||
}
|
||||
case control = <-controlChan:
|
||||
next = true
|
||||
break
|
||||
}
|
||||
if next {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleLogControl(ws *websocket.Conn, controlChan chan controlStruct, errChan chan error) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
logger.Error(err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
logger.Error(err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
msgType, payload, err := ws.ReadMessage()
|
||||
if err != nil && websocket.IsUnexpectedCloseError(err, websocket.CloseNormalClosure) {
|
||||
errChan <- errors.Wrap(err, "error handleLogControl read message")
|
||||
return
|
||||
}
|
||||
for {
|
||||
msgType, payload, err := ws.ReadMessage()
|
||||
if err != nil && websocket.IsUnexpectedCloseError(err, websocket.CloseNormalClosure) {
|
||||
errChan <- errors.Wrap(err, "error handleLogControl read message")
|
||||
return
|
||||
}
|
||||
|
||||
if msgType != websocket.TextMessage {
|
||||
errChan <- errors.New("error handleLogControl message type")
|
||||
return
|
||||
}
|
||||
if msgType != websocket.TextMessage {
|
||||
errChan <- errors.New("error handleLogControl message type")
|
||||
return
|
||||
}
|
||||
|
||||
var msg controlStruct
|
||||
err = json.Unmarshal(payload, &msg)
|
||||
if err != nil {
|
||||
errChan <- errors.Wrap(err, "error ReadWsAndWritePty json.Unmarshal")
|
||||
return
|
||||
}
|
||||
controlChan <- msg
|
||||
}
|
||||
var msg controlStruct
|
||||
err = json.Unmarshal(payload, &msg)
|
||||
if err != nil {
|
||||
errChan <- errors.Wrap(err, "error ReadWsAndWritePty json.Unmarshal")
|
||||
return
|
||||
}
|
||||
controlChan <- msg
|
||||
}
|
||||
}
|
||||
|
||||
func Log(c *gin.Context) {
|
||||
var upGrader = websocket.Upgrader{
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
// upgrade http to websocket
|
||||
ws, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return
|
||||
}
|
||||
var upGrader = websocket.Upgrader{
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
// upgrade http to websocket
|
||||
ws, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
defer ws.Close()
|
||||
defer ws.Close()
|
||||
|
||||
errChan := make(chan error, 1)
|
||||
controlChan := make(chan controlStruct, 1)
|
||||
errChan := make(chan error, 1)
|
||||
controlChan := make(chan controlStruct, 1)
|
||||
|
||||
go tailNginxLog(ws, controlChan, errChan)
|
||||
go handleLogControl(ws, controlChan, errChan)
|
||||
go tailNginxLog(ws, controlChan, errChan)
|
||||
go handleLogControl(ws, controlChan, errChan)
|
||||
|
||||
if err = <-errChan; err != nil {
|
||||
logger.Error(err)
|
||||
_ = ws.WriteMessage(websocket.TextMessage, []byte(err.Error()))
|
||||
return
|
||||
}
|
||||
if err = <-errChan; err != nil {
|
||||
logger.Error(err)
|
||||
_ = ws.WriteMessage(websocket.TextMessage, []byte(err.Error()))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,134 +1,123 @@
|
|||
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/settings"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sashabaranov/go-openai"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
)
|
||||
|
||||
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"`
|
||||
}
|
||||
var json struct {
|
||||
Messages []openai.ChatCompletionMessage `json:"messages"`
|
||||
}
|
||||
|
||||
if !api.BindAndValid(c, &json) {
|
||||
return
|
||||
}
|
||||
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", "*")
|
||||
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", "*")
|
||||
|
||||
if settings.OpenAISettings.Token == "" {
|
||||
c.Stream(func(w io.Writer) bool {
|
||||
c.SSEvent("message", gin.H{
|
||||
"type": "error",
|
||||
"content": "[Error] OpenAI token is empty",
|
||||
})
|
||||
return false
|
||||
})
|
||||
return
|
||||
}
|
||||
config := openai.DefaultConfig(settings.OpenAISettings.Token)
|
||||
|
||||
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.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
|
||||
}
|
||||
|
||||
if settings.OpenAISettings.BaseUrl != "" {
|
||||
config.BaseURL = settings.OpenAISettings.BaseUrl
|
||||
}
|
||||
openaiClient := openai.NewClientWithConfig(config)
|
||||
ctx := context.Background()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
message := fmt.Sprintf("%s", response.Choices[0].Delta.Content)
|
||||
fmt.Printf("%s", message)
|
||||
_ = os.Stdout.Sync()
|
||||
msgChan <- message
|
||||
}
|
||||
}()
|
||||
|
||||
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
|
||||
})
|
||||
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
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue