| package httpstream |
|
|
| import ( |
| "encoding/json" |
| "fmt" |
| "strings" |
|
|
| officialtypes "aurora/typings/official" |
|
|
| "github.com/gin-gonic/gin" |
| ) |
|
|
| |
| func WriteSSEHeader(c *gin.Context) { |
| 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("X-Accel-Buffering", "no") |
| c.Writer.WriteHeader(200) |
| } |
|
|
| |
| func WriteSSEEvent(c *gin.Context, event string, payload interface{}) bool { |
| data, err := json.Marshal(payload) |
| if err != nil { |
| return false |
| } |
| if event != "" { |
| if _, err := c.Writer.WriteString("event: " + event + "\n"); err != nil { |
| return false |
| } |
| } |
| if _, err := c.Writer.WriteString("data: "); err != nil { |
| return false |
| } |
| if _, err := c.Writer.Write(data); err != nil { |
| return false |
| } |
| if _, err := c.Writer.WriteString("\n\n"); err != nil { |
| return false |
| } |
| c.Writer.Flush() |
| return true |
| } |
|
|
| |
| func WriteDone(c *gin.Context) bool { |
| if _, err := c.Writer.WriteString("data: [DONE]\n\n"); err != nil { |
| return false |
| } |
| c.Writer.Flush() |
| return true |
| } |
|
|
| |
| func WriteChatCompletionDone(c *gin.Context, stopSent bool, model string, conversationID string) { |
| if !stopSent { |
| chunk := map[string]interface{}{ |
| "id": "chatcmpl-QXlha2FBbmROaXhpZUFyZUF3ZXNvbWUK", |
| "object": "chat.completion.chunk", |
| "conversation_id": conversationID, |
| "choices": []interface{}{ |
| map[string]interface{}{ |
| "index": 0, |
| "delta": map[string]interface{}{}, |
| "finish_reason": "stop", |
| }, |
| }, |
| } |
| data, _ := json.Marshal(chunk) |
| c.Writer.WriteString("data: " + string(data) + "\n\n") |
| c.Writer.Flush() |
| } |
| c.Writer.WriteString("data: [DONE]\n\n") |
| c.Writer.Flush() |
| } |
|
|
| |
| |
| |
| func WriteUsageChunk(c *gin.Context, model string, inputTokens, outputTokens, cachedTokens, cacheWriteTokens int, msSinceStart, msTTFT int64, ttftSet bool) { |
| chunk := map[string]interface{}{ |
| "id": "chatcmpl-QXlha2FBbmROaXhpZUFyZUF3ZXNvbWUK", |
| "object": "chat.completion.chunk", |
| "created": 0, |
| "model": model, |
| "choices": []interface{}{}, |
| "usage": map[string]interface{}{ |
| "prompt_tokens": inputTokens, |
| "completion_tokens": outputTokens, |
| "total_tokens": inputTokens + outputTokens, |
| "prompt_tokens_details": map[string]interface{}{ |
| "cached_tokens": cachedTokens, |
| "cache_write_tokens": cacheWriteTokens, |
| }, |
| }, |
| "ms_since_start": msSinceStart, |
| } |
| if ttftSet { |
| chunk["ms_ttft"] = msTTFT |
| } |
| data, _ := json.Marshal(chunk) |
| c.Writer.WriteString("data: " + string(data) + "\n\n") |
| c.Writer.Flush() |
| } |
|
|
| |
|
|
| |
| type ImageStreamChunk struct { |
| Object string `json:"object"` |
| Index int `json:"index"` |
| Total int `json:"total"` |
| Created int64 `json:"created"` |
| ProgressText string `json:"progress_text,omitempty"` |
| UpstreamEventType string `json:"upstream_event_type,omitempty"` |
| Model string `json:"model,omitempty"` |
| } |
|
|
| |
| type ImageStreamResult struct { |
| Object string `json:"object"` |
| Index int `json:"index"` |
| Total int `json:"total"` |
| Created int64 `json:"created"` |
| Model string `json:"model,omitempty"` |
| Data []officialtypes.ImageGenerationData `json:"data"` |
| } |
|
|
| |
| type ImageStreamCompleted struct { |
| Object string `json:"object"` |
| Created int64 `json:"created"` |
| Model string `json:"model,omitempty"` |
| Data []officialtypes.ImageGenerationData `json:"data"` |
| } |
|
|
| |
|
|
| |
| func WriteImageStreamHeader(c *gin.Context) { |
| 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("X-Accel-Buffering", "no") |
| c.Writer.WriteHeader(200) |
| } |
|
|
| |
| func WriteImageStreamEvent(c *gin.Context, event string, payload interface{}) bool { |
| data, err := json.Marshal(payload) |
| if err != nil { |
| return false |
| } |
| if event != "" { |
| if _, err := c.Writer.WriteString("event: " + event + "\n"); err != nil { |
| return false |
| } |
| } |
| if _, err := c.Writer.WriteString("data: "); err != nil { |
| return false |
| } |
| if _, err := c.Writer.Write(data); err != nil { |
| return false |
| } |
| if _, err := c.Writer.WriteString("\n\n"); err != nil { |
| return false |
| } |
| c.Writer.Flush() |
| return true |
| } |
|
|
| |
| func WriteImageStreamDone(c *gin.Context) bool { |
| if _, err := c.Writer.WriteString("data: [DONE]\n\n"); err != nil { |
| return false |
| } |
| c.Writer.Flush() |
| return true |
| } |
|
|
| |
| func WriteImageStreamChunk(c *gin.Context, index, total int, model string) { |
| WriteImageStreamEvent(c, "image.generation.chunk", ImageStreamChunk{ |
| Object: "image.generation.chunk", |
| Index: index, |
| Total: total, |
| Created: 0, |
| Model: model, |
| ProgressText: fmt.Sprintf("Generating image %d/%d ...", index+1, total), |
| }) |
| } |
|
|
| |
| func WriteImageStreamResult(c *gin.Context, index, total int, model string, data []officialtypes.ImageGenerationData) { |
| WriteImageStreamEvent(c, "image.generation.result", ImageStreamResult{ |
| Object: "image.generation.result", |
| Index: index, |
| Total: total, |
| Created: 0, |
| Model: model, |
| Data: data, |
| }) |
| } |
|
|
| |
| func WriteImageStreamCompleted(c *gin.Context, model string, data []officialtypes.ImageGenerationData) { |
| WriteImageStreamEvent(c, "image.generation.completed", ImageStreamCompleted{ |
| Object: "image.generation.completed", |
| Created: 0, |
| Model: model, |
| Data: data, |
| }) |
| } |
|
|
| |
| func WriteImageStreamError(c *gin.Context, index, total int, message string) { |
| WriteImageStreamEvent(c, "image.generation.error", map[string]interface{}{ |
| "object": "image.generation.error", |
| "index": index, |
| "total": total, |
| "message": message, |
| }) |
| } |
|
|
| |
|
|
| |
| |
| func RequestStreamFlag(c *gin.Context, jsonStream bool) bool { |
| if jsonStream { |
| return true |
| } |
| if v := strings.ToLower(strings.TrimSpace(c.Query("stream"))); v == "true" || v == "1" || v == "yes" { |
| return true |
| } |
| if v := strings.ToLower(strings.TrimSpace(c.PostForm("stream"))); v == "true" || v == "1" || v == "yes" { |
| return true |
| } |
| return false |
| } |
|
|
| |
| func IsStreamTrue(v string) bool { |
| switch strings.ToLower(strings.TrimSpace(v)) { |
| case "true", "1", "yes", "on": |
| return true |
| } |
| return false |
| } |
|
|