Merge pull request #1122 from akkuman/feat/stream-tts

feat: streaming response for tts
This commit is contained in:
Calcium-Ion
2025-05-31 18:44:48 +08:00
committed by GitHub

View File

@@ -273,36 +273,25 @@ func OpenaiHandler(c *gin.Context, resp *http.Response, info *relaycommon.RelayI
} }
func OpenaiTTSHandler(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (*dto.OpenAIErrorWithStatusCode, *dto.Usage) { func OpenaiTTSHandler(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (*dto.OpenAIErrorWithStatusCode, *dto.Usage) {
responseBody, err := io.ReadAll(resp.Body) // the status code has been judged before, if there is a body reading failure,
if err != nil { // it should be regarded as a non-recoverable error, so it should not return err for external retry.
return service.OpenAIErrorWrapper(err, "read_response_body_failed", http.StatusInternalServerError), nil // Analogous to nginx's load balancing, it will only retry if it can't be requested or
} // if the upstream returns a specific status code, once the upstream has already written the header,
err = resp.Body.Close() // the subsequent failure of the response body should be regarded as a non-recoverable error,
if err != nil { // and can be terminated directly.
return service.OpenAIErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil defer resp.Body.Close()
} usage := &dto.Usage{}
// Reset response body usage.PromptTokens = info.PromptTokens
resp.Body = io.NopCloser(bytes.NewBuffer(responseBody)) usage.TotalTokens = info.PromptTokens
// We shouldn't set the header before we parse the response body, because the parse part may fail.
// And then we will have to send an error response, but in this case, the header has already been set.
// So the httpClient will be confused by the response.
// For example, Postman will report error, and we cannot check the response at all.
for k, v := range resp.Header { for k, v := range resp.Header {
c.Writer.Header().Set(k, v[0]) c.Writer.Header().Set(k, v[0])
} }
c.Writer.WriteHeader(resp.StatusCode) c.Writer.WriteHeader(resp.StatusCode)
_, err = io.Copy(c.Writer, resp.Body) c.Writer.WriteHeaderNow()
_, err := io.Copy(c.Writer, resp.Body)
if err != nil { if err != nil {
return service.OpenAIErrorWrapper(err, "copy_response_body_failed", http.StatusInternalServerError), nil common.LogError(c, err.Error())
} }
err = resp.Body.Close()
if err != nil {
return service.OpenAIErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil
}
usage := &dto.Usage{}
usage.PromptTokens = info.PromptTokens
usage.TotalTokens = info.PromptTokens
return nil, usage return nil, usage
} }