| package helper |
|
|
| import ( |
| "errors" |
| "fmt" |
| "net/http" |
|
|
| "github.com/QuantumNous/new-api/common" |
| "github.com/QuantumNous/new-api/dto" |
| "github.com/QuantumNous/new-api/logger" |
| "github.com/QuantumNous/new-api/types" |
|
|
| "github.com/gin-gonic/gin" |
| "github.com/gorilla/websocket" |
| ) |
|
|
| func FlushWriter(c *gin.Context) error { |
| if c.Writer == nil { |
| return nil |
| } |
| if flusher, ok := c.Writer.(http.Flusher); ok { |
| flusher.Flush() |
| return nil |
| } |
| return errors.New("streaming error: flusher not found") |
| } |
|
|
| func SetEventStreamHeaders(c *gin.Context) { |
| |
| if _, exists := c.Get("event_stream_headers_set"); exists { |
| return |
| } |
|
|
| |
| c.Set("event_stream_headers_set", true) |
|
|
| c.Writer.Header().Set("Content-Type", "text/event-stream") |
| c.Writer.Header().Set("Cache-Control", "no-cache") |
| c.Writer.Header().Set("Connection", "keep-alive") |
| c.Writer.Header().Set("Transfer-Encoding", "chunked") |
| c.Writer.Header().Set("X-Accel-Buffering", "no") |
| } |
|
|
| func ClaudeData(c *gin.Context, resp dto.ClaudeResponse) error { |
| jsonData, err := common.Marshal(resp) |
| if err != nil { |
| common.SysError("error marshalling stream response: " + err.Error()) |
| } else { |
| c.Render(-1, common.CustomEvent{Data: fmt.Sprintf("event: %s\n", resp.Type)}) |
| c.Render(-1, common.CustomEvent{Data: "data: " + string(jsonData)}) |
| } |
| _ = FlushWriter(c) |
| return nil |
| } |
|
|
| func ClaudeChunkData(c *gin.Context, resp dto.ClaudeResponse, data string) { |
| c.Render(-1, common.CustomEvent{Data: fmt.Sprintf("event: %s\n", resp.Type)}) |
| c.Render(-1, common.CustomEvent{Data: fmt.Sprintf("data: %s\n", data)}) |
| _ = FlushWriter(c) |
| } |
|
|
| func ResponseChunkData(c *gin.Context, resp dto.ResponsesStreamResponse, data string) { |
| c.Render(-1, common.CustomEvent{Data: fmt.Sprintf("event: %s\n", resp.Type)}) |
| c.Render(-1, common.CustomEvent{Data: fmt.Sprintf("data: %s", data)}) |
| _ = FlushWriter(c) |
| } |
|
|
| func StringData(c *gin.Context, str string) error { |
| |
| |
| c.Render(-1, common.CustomEvent{Data: "data: " + str}) |
| _ = FlushWriter(c) |
| return nil |
| } |
|
|
| func PingData(c *gin.Context) error { |
| c.Writer.Write([]byte(": PING\n\n")) |
| _ = FlushWriter(c) |
| return nil |
| } |
|
|
| func ObjectData(c *gin.Context, object interface{}) error { |
| if object == nil { |
| return errors.New("object is nil") |
| } |
| jsonData, err := common.Marshal(object) |
| if err != nil { |
| return fmt.Errorf("error marshalling object: %w", err) |
| } |
| return StringData(c, string(jsonData)) |
| } |
|
|
| func Done(c *gin.Context) { |
| _ = StringData(c, "[DONE]") |
| } |
|
|
| func WssString(c *gin.Context, ws *websocket.Conn, str string) error { |
| if ws == nil { |
| logger.LogError(c, "websocket connection is nil") |
| return errors.New("websocket connection is nil") |
| } |
| |
| return ws.WriteMessage(1, []byte(str)) |
| } |
|
|
| func WssObject(c *gin.Context, ws *websocket.Conn, object interface{}) error { |
| jsonData, err := common.Marshal(object) |
| if err != nil { |
| return fmt.Errorf("error marshalling object: %w", err) |
| } |
| if ws == nil { |
| logger.LogError(c, "websocket connection is nil") |
| return errors.New("websocket connection is nil") |
| } |
| |
| return ws.WriteMessage(1, jsonData) |
| } |
|
|
| func WssError(c *gin.Context, ws *websocket.Conn, openaiError types.OpenAIError) { |
| if ws == nil { |
| return |
| } |
| errorObj := &dto.RealtimeEvent{ |
| Type: "error", |
| EventId: GetLocalRealtimeID(c), |
| Error: &openaiError, |
| } |
| _ = WssObject(c, ws, errorObj) |
| } |
|
|
| func GetResponseID(c *gin.Context) string { |
| logID := c.GetString(common.RequestIdKey) |
| return fmt.Sprintf("chatcmpl-%s", logID) |
| } |
|
|
| func GetLocalRealtimeID(c *gin.Context) string { |
| logID := c.GetString(common.RequestIdKey) |
| return fmt.Sprintf("evt_%s", logID) |
| } |
|
|
| func GenerateStartEmptyResponse(id string, createAt int64, model string, systemFingerprint *string) *dto.ChatCompletionsStreamResponse { |
| return &dto.ChatCompletionsStreamResponse{ |
| Id: id, |
| Object: "chat.completion.chunk", |
| Created: createAt, |
| Model: model, |
| SystemFingerprint: systemFingerprint, |
| Choices: []dto.ChatCompletionsStreamResponseChoice{ |
| { |
| Delta: dto.ChatCompletionsStreamResponseChoiceDelta{ |
| Role: "assistant", |
| Content: common.GetPointer(""), |
| }, |
| }, |
| }, |
| } |
| } |
|
|
| func GenerateStopResponse(id string, createAt int64, model string, finishReason string) *dto.ChatCompletionsStreamResponse { |
| return &dto.ChatCompletionsStreamResponse{ |
| Id: id, |
| Object: "chat.completion.chunk", |
| Created: createAt, |
| Model: model, |
| SystemFingerprint: nil, |
| Choices: []dto.ChatCompletionsStreamResponseChoice{ |
| { |
| FinishReason: &finishReason, |
| }, |
| }, |
| } |
| } |
|
|
| func GenerateFinalUsageResponse(id string, createAt int64, model string, usage dto.Usage) *dto.ChatCompletionsStreamResponse { |
| return &dto.ChatCompletionsStreamResponse{ |
| Id: id, |
| Object: "chat.completion.chunk", |
| Created: createAt, |
| Model: model, |
| SystemFingerprint: nil, |
| Choices: make([]dto.ChatCompletionsStreamResponseChoice, 0), |
| Usage: &usage, |
| } |
| } |
|
|