mirror of
https://github.com/songquanpeng/one-api.git
synced 2025-11-06 16:53:42 +08:00
Compare commits
7 Commits
v0.6.7
...
v0.6.8-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b21b3b5b46 | ||
|
|
ae1cd29f94 | ||
|
|
f25aaf7752 | ||
|
|
b70a07e814 | ||
|
|
34cb147a74 | ||
|
|
8cc1ee6360 | ||
|
|
5a58426859 |
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@@ -45,17 +45,15 @@ jobs:
|
|||||||
|
|
||||||
code_coverage:
|
code_coverage:
|
||||||
name: "Code coverage report"
|
name: "Code coverage report"
|
||||||
if: github.event_name == 'pull_request' # Do not run when workflow is triggered by push to main branch
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: unit_tests # Depends on the artifact uploaded by the "unit_tests" job
|
needs: unit_tests # Depends on the artifact uploaded by the "unit_tests" job
|
||||||
steps:
|
steps:
|
||||||
- uses: fgrosse/go-coverage-report@v1.0.2 # Consider using a Git revision for maximum security
|
- uses: codecov/codecov-action@v4
|
||||||
with:
|
with:
|
||||||
coverage-artifact-name: "code-coverage" # can be omitted if you used this default value
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
coverage-file-name: "coverage.txt" # can be omitted if you used this default value
|
|
||||||
|
|
||||||
commit_lint:
|
commit_lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: wagoid/commitlint-github-action@v6
|
- uses: wagoid/commitlint-github-action@v6
|
||||||
|
|||||||
@@ -19,4 +19,5 @@ const (
|
|||||||
TokenName = "token_name"
|
TokenName = "token_name"
|
||||||
BaseURL = "base_url"
|
BaseURL = "base_url"
|
||||||
AvailableModels = "available_models"
|
AvailableModels = "available_models"
|
||||||
|
KeyRequestBody = "key_request_body"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,14 +4,13 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/songquanpeng/one-api/common/ctxkey"
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const KeyRequestBody = "key_request_body"
|
|
||||||
|
|
||||||
func GetRequestBody(c *gin.Context) ([]byte, error) {
|
func GetRequestBody(c *gin.Context) ([]byte, error) {
|
||||||
requestBody, _ := c.Get(KeyRequestBody)
|
requestBody, _ := c.Get(ctxkey.KeyRequestBody)
|
||||||
if requestBody != nil {
|
if requestBody != nil {
|
||||||
return requestBody.([]byte), nil
|
return requestBody.([]byte), nil
|
||||||
}
|
}
|
||||||
@@ -20,7 +19,7 @@ func GetRequestBody(c *gin.Context) ([]byte, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
_ = c.Request.Body.Close()
|
_ = c.Request.Body.Close()
|
||||||
c.Set(KeyRequestBody, requestBody)
|
c.Set(ctxkey.KeyRequestBody, requestBody)
|
||||||
return requestBody.([]byte), nil
|
return requestBody.([]byte), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
29
common/render/render.go
Normal file
29
common/render/render.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package render
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/songquanpeng/one-api/common"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func StringData(c *gin.Context, str string) {
|
||||||
|
str = strings.TrimPrefix(str, "data: ")
|
||||||
|
str = strings.TrimSuffix(str, "\r")
|
||||||
|
c.Render(-1, common.CustomEvent{Data: "data: " + str})
|
||||||
|
c.Writer.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func ObjectData(c *gin.Context, object interface{}) error {
|
||||||
|
jsonData, err := json.Marshal(object)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error marshalling object: %w", err)
|
||||||
|
}
|
||||||
|
StringData(c, string(jsonData))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Done(c *gin.Context) {
|
||||||
|
StringData(c, "[DONE]")
|
||||||
|
}
|
||||||
@@ -48,7 +48,7 @@ func Relay(c *gin.Context) {
|
|||||||
logger.Debugf(ctx, "request body: %s", string(requestBody))
|
logger.Debugf(ctx, "request body: %s", string(requestBody))
|
||||||
}
|
}
|
||||||
channelId := c.GetInt(ctxkey.ChannelId)
|
channelId := c.GetInt(ctxkey.ChannelId)
|
||||||
userId := c.GetInt("id")
|
userId := c.GetInt(ctxkey.Id)
|
||||||
bizErr := relayHelper(c, relayMode)
|
bizErr := relayHelper(c, relayMode)
|
||||||
if bizErr == nil {
|
if bizErr == nil {
|
||||||
monitor.Emit(channelId, true)
|
monitor.Emit(channelId, true)
|
||||||
|
|||||||
4
go.mod
4
go.mod
@@ -24,7 +24,7 @@ require (
|
|||||||
github.com/smartystreets/goconvey v1.8.1
|
github.com/smartystreets/goconvey v1.8.1
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.9.0
|
||||||
golang.org/x/crypto v0.23.0
|
golang.org/x/crypto v0.23.0
|
||||||
golang.org/x/image v0.16.0
|
golang.org/x/image v0.18.0
|
||||||
gorm.io/driver/mysql v1.5.6
|
gorm.io/driver/mysql v1.5.6
|
||||||
gorm.io/driver/postgres v1.5.7
|
gorm.io/driver/postgres v1.5.7
|
||||||
gorm.io/driver/sqlite v1.5.5
|
gorm.io/driver/sqlite v1.5.5
|
||||||
@@ -80,7 +80,7 @@ require (
|
|||||||
golang.org/x/net v0.25.0 // indirect
|
golang.org/x/net v0.25.0 // indirect
|
||||||
golang.org/x/sync v0.7.0 // indirect
|
golang.org/x/sync v0.7.0 // indirect
|
||||||
golang.org/x/sys v0.20.0 // indirect
|
golang.org/x/sys v0.20.0 // indirect
|
||||||
golang.org/x/text v0.15.0 // indirect
|
golang.org/x/text v0.16.0 // indirect
|
||||||
google.golang.org/protobuf v1.34.1 // indirect
|
google.golang.org/protobuf v1.34.1 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
8
go.sum
8
go.sum
@@ -154,8 +154,8 @@ golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
|||||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||||
golang.org/x/image v0.16.0 h1:9kloLAKhUufZhA12l5fwnx2NZW39/we1UhBesW433jw=
|
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
|
||||||
golang.org/x/image v0.16.0/go.mod h1:ugSZItdV4nOxyqp56HmXwH0Ry0nBCpjnZdpDaIHdoPs=
|
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
||||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||||
@@ -164,8 +164,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
|
|||||||
@@ -4,6 +4,12 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/songquanpeng/one-api/common/render"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/songquanpeng/one-api/common"
|
"github.com/songquanpeng/one-api/common"
|
||||||
"github.com/songquanpeng/one-api/common/helper"
|
"github.com/songquanpeng/one-api/common/helper"
|
||||||
@@ -12,10 +18,6 @@ import (
|
|||||||
"github.com/songquanpeng/one-api/relay/adaptor/openai"
|
"github.com/songquanpeng/one-api/relay/adaptor/openai"
|
||||||
"github.com/songquanpeng/one-api/relay/constant"
|
"github.com/songquanpeng/one-api/relay/constant"
|
||||||
"github.com/songquanpeng/one-api/relay/model"
|
"github.com/songquanpeng/one-api/relay/model"
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// https://docs.aiproxy.io/dev/library#使用已经定制好的知识库进行对话问答
|
// https://docs.aiproxy.io/dev/library#使用已经定制好的知识库进行对话问答
|
||||||
@@ -89,6 +91,7 @@ func streamResponseAIProxyLibrary2OpenAI(response *LibraryStreamResponse) *opena
|
|||||||
|
|
||||||
func StreamHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusCode, *model.Usage) {
|
func StreamHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusCode, *model.Usage) {
|
||||||
var usage model.Usage
|
var usage model.Usage
|
||||||
|
var documents []LibraryDocument
|
||||||
scanner := bufio.NewScanner(resp.Body)
|
scanner := bufio.NewScanner(resp.Body)
|
||||||
scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
||||||
if atEOF && len(data) == 0 {
|
if atEOF && len(data) == 0 {
|
||||||
@@ -102,60 +105,48 @@ func StreamHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusC
|
|||||||
}
|
}
|
||||||
return 0, nil, nil
|
return 0, nil, nil
|
||||||
})
|
})
|
||||||
dataChan := make(chan string)
|
|
||||||
stopChan := make(chan bool)
|
|
||||||
go func() {
|
|
||||||
for scanner.Scan() {
|
|
||||||
data := scanner.Text()
|
|
||||||
if len(data) < 5 { // ignore blank line or wrong format
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if data[:5] != "data:" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
data = data[5:]
|
|
||||||
dataChan <- data
|
|
||||||
}
|
|
||||||
stopChan <- true
|
|
||||||
}()
|
|
||||||
common.SetEventStreamHeaders(c)
|
common.SetEventStreamHeaders(c)
|
||||||
var documents []LibraryDocument
|
|
||||||
c.Stream(func(w io.Writer) bool {
|
for scanner.Scan() {
|
||||||
select {
|
data := scanner.Text()
|
||||||
case data := <-dataChan:
|
if len(data) < 5 || data[:5] != "data:" {
|
||||||
var AIProxyLibraryResponse LibraryStreamResponse
|
continue
|
||||||
err := json.Unmarshal([]byte(data), &AIProxyLibraryResponse)
|
|
||||||
if err != nil {
|
|
||||||
logger.SysError("error unmarshalling stream response: " + err.Error())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if len(AIProxyLibraryResponse.Documents) != 0 {
|
|
||||||
documents = AIProxyLibraryResponse.Documents
|
|
||||||
}
|
|
||||||
response := streamResponseAIProxyLibrary2OpenAI(&AIProxyLibraryResponse)
|
|
||||||
jsonResponse, err := json.Marshal(response)
|
|
||||||
if err != nil {
|
|
||||||
logger.SysError("error marshalling stream response: " + err.Error())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
c.Render(-1, common.CustomEvent{Data: "data: " + string(jsonResponse)})
|
|
||||||
return true
|
|
||||||
case <-stopChan:
|
|
||||||
response := documentsAIProxyLibrary(documents)
|
|
||||||
jsonResponse, err := json.Marshal(response)
|
|
||||||
if err != nil {
|
|
||||||
logger.SysError("error marshalling stream response: " + err.Error())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
c.Render(-1, common.CustomEvent{Data: "data: " + string(jsonResponse)})
|
|
||||||
c.Render(-1, common.CustomEvent{Data: "data: [DONE]"})
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
})
|
data = data[5:]
|
||||||
err := resp.Body.Close()
|
|
||||||
|
var AIProxyLibraryResponse LibraryStreamResponse
|
||||||
|
err := json.Unmarshal([]byte(data), &AIProxyLibraryResponse)
|
||||||
|
if err != nil {
|
||||||
|
logger.SysError("error unmarshalling stream response: " + err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(AIProxyLibraryResponse.Documents) != 0 {
|
||||||
|
documents = AIProxyLibraryResponse.Documents
|
||||||
|
}
|
||||||
|
response := streamResponseAIProxyLibrary2OpenAI(&AIProxyLibraryResponse)
|
||||||
|
err = render.ObjectData(c, response)
|
||||||
|
if err != nil {
|
||||||
|
logger.SysError(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
logger.SysError("error reading stream: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
response := documentsAIProxyLibrary(documents)
|
||||||
|
err := render.ObjectData(c, response)
|
||||||
|
if err != nil {
|
||||||
|
logger.SysError(err.Error())
|
||||||
|
}
|
||||||
|
render.Done(c)
|
||||||
|
|
||||||
|
err = resp.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil
|
return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, &usage
|
return nil, &usage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,15 +3,17 @@ package ali
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"github.com/songquanpeng/one-api/common/render"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/songquanpeng/one-api/common"
|
"github.com/songquanpeng/one-api/common"
|
||||||
"github.com/songquanpeng/one-api/common/helper"
|
"github.com/songquanpeng/one-api/common/helper"
|
||||||
"github.com/songquanpeng/one-api/common/logger"
|
"github.com/songquanpeng/one-api/common/logger"
|
||||||
"github.com/songquanpeng/one-api/relay/adaptor/openai"
|
"github.com/songquanpeng/one-api/relay/adaptor/openai"
|
||||||
"github.com/songquanpeng/one-api/relay/model"
|
"github.com/songquanpeng/one-api/relay/model"
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// https://help.aliyun.com/document_detail/613695.html?spm=a2c4g.2399480.0.0.1adb778fAdzP9w#341800c0f8w0r
|
// https://help.aliyun.com/document_detail/613695.html?spm=a2c4g.2399480.0.0.1adb778fAdzP9w#341800c0f8w0r
|
||||||
@@ -181,56 +183,43 @@ func StreamHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusC
|
|||||||
}
|
}
|
||||||
return 0, nil, nil
|
return 0, nil, nil
|
||||||
})
|
})
|
||||||
dataChan := make(chan string)
|
|
||||||
stopChan := make(chan bool)
|
|
||||||
go func() {
|
|
||||||
for scanner.Scan() {
|
|
||||||
data := scanner.Text()
|
|
||||||
if len(data) < 5 { // ignore blank line or wrong format
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if data[:5] != "data:" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
data = data[5:]
|
|
||||||
dataChan <- data
|
|
||||||
}
|
|
||||||
stopChan <- true
|
|
||||||
}()
|
|
||||||
common.SetEventStreamHeaders(c)
|
common.SetEventStreamHeaders(c)
|
||||||
//lastResponseText := ""
|
|
||||||
c.Stream(func(w io.Writer) bool {
|
for scanner.Scan() {
|
||||||
select {
|
data := scanner.Text()
|
||||||
case data := <-dataChan:
|
if len(data) < 5 || data[:5] != "data:" {
|
||||||
var aliResponse ChatResponse
|
continue
|
||||||
err := json.Unmarshal([]byte(data), &aliResponse)
|
|
||||||
if err != nil {
|
|
||||||
logger.SysError("error unmarshalling stream response: " + err.Error())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if aliResponse.Usage.OutputTokens != 0 {
|
|
||||||
usage.PromptTokens = aliResponse.Usage.InputTokens
|
|
||||||
usage.CompletionTokens = aliResponse.Usage.OutputTokens
|
|
||||||
usage.TotalTokens = aliResponse.Usage.InputTokens + aliResponse.Usage.OutputTokens
|
|
||||||
}
|
|
||||||
response := streamResponseAli2OpenAI(&aliResponse)
|
|
||||||
if response == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
//response.Choices[0].Delta.Content = strings.TrimPrefix(response.Choices[0].Delta.Content, lastResponseText)
|
|
||||||
//lastResponseText = aliResponse.Output.Text
|
|
||||||
jsonResponse, err := json.Marshal(response)
|
|
||||||
if err != nil {
|
|
||||||
logger.SysError("error marshalling stream response: " + err.Error())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
c.Render(-1, common.CustomEvent{Data: "data: " + string(jsonResponse)})
|
|
||||||
return true
|
|
||||||
case <-stopChan:
|
|
||||||
c.Render(-1, common.CustomEvent{Data: "data: [DONE]"})
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
})
|
data = data[5:]
|
||||||
|
|
||||||
|
var aliResponse ChatResponse
|
||||||
|
err := json.Unmarshal([]byte(data), &aliResponse)
|
||||||
|
if err != nil {
|
||||||
|
logger.SysError("error unmarshalling stream response: " + err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if aliResponse.Usage.OutputTokens != 0 {
|
||||||
|
usage.PromptTokens = aliResponse.Usage.InputTokens
|
||||||
|
usage.CompletionTokens = aliResponse.Usage.OutputTokens
|
||||||
|
usage.TotalTokens = aliResponse.Usage.InputTokens + aliResponse.Usage.OutputTokens
|
||||||
|
}
|
||||||
|
response := streamResponseAli2OpenAI(&aliResponse)
|
||||||
|
if response == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = render.ObjectData(c, response)
|
||||||
|
if err != nil {
|
||||||
|
logger.SysError(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
logger.SysError("error reading stream: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Done(c)
|
||||||
|
|
||||||
err := resp.Body.Close()
|
err := resp.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil
|
return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil
|
||||||
|
|||||||
@@ -5,4 +5,5 @@ var ModelList = []string{
|
|||||||
"claude-3-haiku-20240307",
|
"claude-3-haiku-20240307",
|
||||||
"claude-3-sonnet-20240229",
|
"claude-3-sonnet-20240229",
|
||||||
"claude-3-opus-20240229",
|
"claude-3-opus-20240229",
|
||||||
|
"claude-3-5-sonnet-20240620",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/songquanpeng/one-api/common/render"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -169,64 +170,59 @@ func StreamHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusC
|
|||||||
}
|
}
|
||||||
return 0, nil, nil
|
return 0, nil, nil
|
||||||
})
|
})
|
||||||
dataChan := make(chan string)
|
|
||||||
stopChan := make(chan bool)
|
|
||||||
go func() {
|
|
||||||
for scanner.Scan() {
|
|
||||||
data := scanner.Text()
|
|
||||||
if len(data) < 6 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !strings.HasPrefix(data, "data:") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
data = strings.TrimPrefix(data, "data:")
|
|
||||||
dataChan <- data
|
|
||||||
}
|
|
||||||
stopChan <- true
|
|
||||||
}()
|
|
||||||
common.SetEventStreamHeaders(c)
|
common.SetEventStreamHeaders(c)
|
||||||
|
|
||||||
var usage model.Usage
|
var usage model.Usage
|
||||||
var modelName string
|
var modelName string
|
||||||
var id string
|
var id string
|
||||||
c.Stream(func(w io.Writer) bool {
|
|
||||||
select {
|
for scanner.Scan() {
|
||||||
case data := <-dataChan:
|
data := scanner.Text()
|
||||||
// some implementations may add \r at the end of data
|
if len(data) < 6 || !strings.HasPrefix(data, "data:") {
|
||||||
data = strings.TrimSpace(data)
|
continue
|
||||||
var claudeResponse StreamResponse
|
|
||||||
err := json.Unmarshal([]byte(data), &claudeResponse)
|
|
||||||
if err != nil {
|
|
||||||
logger.SysError("error unmarshalling stream response: " + err.Error())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
response, meta := StreamResponseClaude2OpenAI(&claudeResponse)
|
|
||||||
if meta != nil {
|
|
||||||
usage.PromptTokens += meta.Usage.InputTokens
|
|
||||||
usage.CompletionTokens += meta.Usage.OutputTokens
|
|
||||||
modelName = meta.Model
|
|
||||||
id = fmt.Sprintf("chatcmpl-%s", meta.Id)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if response == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
response.Id = id
|
|
||||||
response.Model = modelName
|
|
||||||
response.Created = createdTime
|
|
||||||
jsonStr, err := json.Marshal(response)
|
|
||||||
if err != nil {
|
|
||||||
logger.SysError("error marshalling stream response: " + err.Error())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
c.Render(-1, common.CustomEvent{Data: "data: " + string(jsonStr)})
|
|
||||||
return true
|
|
||||||
case <-stopChan:
|
|
||||||
c.Render(-1, common.CustomEvent{Data: "data: [DONE]"})
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
})
|
data = strings.TrimPrefix(data, "data:")
|
||||||
_ = resp.Body.Close()
|
data = strings.TrimSpace(data)
|
||||||
|
|
||||||
|
var claudeResponse StreamResponse
|
||||||
|
err := json.Unmarshal([]byte(data), &claudeResponse)
|
||||||
|
if err != nil {
|
||||||
|
logger.SysError("error unmarshalling stream response: " + err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
response, meta := StreamResponseClaude2OpenAI(&claudeResponse)
|
||||||
|
if meta != nil {
|
||||||
|
usage.PromptTokens += meta.Usage.InputTokens
|
||||||
|
usage.CompletionTokens += meta.Usage.OutputTokens
|
||||||
|
modelName = meta.Model
|
||||||
|
id = fmt.Sprintf("chatcmpl-%s", meta.Id)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if response == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
response.Id = id
|
||||||
|
response.Model = modelName
|
||||||
|
response.Created = createdTime
|
||||||
|
err = render.ObjectData(c, response)
|
||||||
|
if err != nil {
|
||||||
|
logger.SysError(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
logger.SysError("error reading stream: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Done(c)
|
||||||
|
|
||||||
|
err := resp.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil
|
||||||
|
}
|
||||||
return nil, &usage
|
return nil, &usage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,12 +33,13 @@ func wrapErr(err error) *relaymodel.ErrorWithStatusCode {
|
|||||||
|
|
||||||
// https://docs.aws.amazon.com/bedrock/latest/userguide/model-ids.html
|
// https://docs.aws.amazon.com/bedrock/latest/userguide/model-ids.html
|
||||||
var awsModelIDMap = map[string]string{
|
var awsModelIDMap = map[string]string{
|
||||||
"claude-instant-1.2": "anthropic.claude-instant-v1",
|
"claude-instant-1.2": "anthropic.claude-instant-v1",
|
||||||
"claude-2.0": "anthropic.claude-v2",
|
"claude-2.0": "anthropic.claude-v2",
|
||||||
"claude-2.1": "anthropic.claude-v2:1",
|
"claude-2.1": "anthropic.claude-v2:1",
|
||||||
"claude-3-sonnet-20240229": "anthropic.claude-3-sonnet-20240229-v1:0",
|
"claude-3-sonnet-20240229": "anthropic.claude-3-sonnet-20240229-v1:0",
|
||||||
"claude-3-opus-20240229": "anthropic.claude-3-opus-20240229-v1:0",
|
"claude-3-5-sonnet-20240620": "anthropic.claude-3-5-sonnet-20240620-v1:0",
|
||||||
"claude-3-haiku-20240307": "anthropic.claude-3-haiku-20240307-v1:0",
|
"claude-3-opus-20240229": "anthropic.claude-3-opus-20240229-v1:0",
|
||||||
|
"claude-3-haiku-20240307": "anthropic.claude-3-haiku-20240307-v1:0",
|
||||||
}
|
}
|
||||||
|
|
||||||
func awsModelID(requestModel string) (string, error) {
|
func awsModelID(requestModel string) (string, error) {
|
||||||
|
|||||||
@@ -5,6 +5,13 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/songquanpeng/one-api/common/render"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/songquanpeng/one-api/common"
|
"github.com/songquanpeng/one-api/common"
|
||||||
"github.com/songquanpeng/one-api/common/client"
|
"github.com/songquanpeng/one-api/common/client"
|
||||||
@@ -12,11 +19,6 @@ import (
|
|||||||
"github.com/songquanpeng/one-api/relay/adaptor/openai"
|
"github.com/songquanpeng/one-api/relay/adaptor/openai"
|
||||||
"github.com/songquanpeng/one-api/relay/constant"
|
"github.com/songquanpeng/one-api/relay/constant"
|
||||||
"github.com/songquanpeng/one-api/relay/model"
|
"github.com/songquanpeng/one-api/relay/model"
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// https://cloud.baidu.com/doc/WENXINWORKSHOP/s/flfmc9do2
|
// https://cloud.baidu.com/doc/WENXINWORKSHOP/s/flfmc9do2
|
||||||
@@ -137,59 +139,41 @@ func embeddingResponseBaidu2OpenAI(response *EmbeddingResponse) *openai.Embeddin
|
|||||||
func StreamHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusCode, *model.Usage) {
|
func StreamHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusCode, *model.Usage) {
|
||||||
var usage model.Usage
|
var usage model.Usage
|
||||||
scanner := bufio.NewScanner(resp.Body)
|
scanner := bufio.NewScanner(resp.Body)
|
||||||
scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
scanner.Split(bufio.ScanLines)
|
||||||
if atEOF && len(data) == 0 {
|
|
||||||
return 0, nil, nil
|
|
||||||
}
|
|
||||||
if i := strings.Index(string(data), "\n"); i >= 0 {
|
|
||||||
return i + 1, data[0:i], nil
|
|
||||||
}
|
|
||||||
if atEOF {
|
|
||||||
return len(data), data, nil
|
|
||||||
}
|
|
||||||
return 0, nil, nil
|
|
||||||
})
|
|
||||||
dataChan := make(chan string)
|
|
||||||
stopChan := make(chan bool)
|
|
||||||
go func() {
|
|
||||||
for scanner.Scan() {
|
|
||||||
data := scanner.Text()
|
|
||||||
if len(data) < 6 { // ignore blank line or wrong format
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
data = data[6:]
|
|
||||||
dataChan <- data
|
|
||||||
}
|
|
||||||
stopChan <- true
|
|
||||||
}()
|
|
||||||
common.SetEventStreamHeaders(c)
|
common.SetEventStreamHeaders(c)
|
||||||
c.Stream(func(w io.Writer) bool {
|
|
||||||
select {
|
for scanner.Scan() {
|
||||||
case data := <-dataChan:
|
data := scanner.Text()
|
||||||
var baiduResponse ChatStreamResponse
|
if len(data) < 6 {
|
||||||
err := json.Unmarshal([]byte(data), &baiduResponse)
|
continue
|
||||||
if err != nil {
|
|
||||||
logger.SysError("error unmarshalling stream response: " + err.Error())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if baiduResponse.Usage.TotalTokens != 0 {
|
|
||||||
usage.TotalTokens = baiduResponse.Usage.TotalTokens
|
|
||||||
usage.PromptTokens = baiduResponse.Usage.PromptTokens
|
|
||||||
usage.CompletionTokens = baiduResponse.Usage.TotalTokens - baiduResponse.Usage.PromptTokens
|
|
||||||
}
|
|
||||||
response := streamResponseBaidu2OpenAI(&baiduResponse)
|
|
||||||
jsonResponse, err := json.Marshal(response)
|
|
||||||
if err != nil {
|
|
||||||
logger.SysError("error marshalling stream response: " + err.Error())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
c.Render(-1, common.CustomEvent{Data: "data: " + string(jsonResponse)})
|
|
||||||
return true
|
|
||||||
case <-stopChan:
|
|
||||||
c.Render(-1, common.CustomEvent{Data: "data: [DONE]"})
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
})
|
data = data[6:]
|
||||||
|
|
||||||
|
var baiduResponse ChatStreamResponse
|
||||||
|
err := json.Unmarshal([]byte(data), &baiduResponse)
|
||||||
|
if err != nil {
|
||||||
|
logger.SysError("error unmarshalling stream response: " + err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if baiduResponse.Usage.TotalTokens != 0 {
|
||||||
|
usage.TotalTokens = baiduResponse.Usage.TotalTokens
|
||||||
|
usage.PromptTokens = baiduResponse.Usage.PromptTokens
|
||||||
|
usage.CompletionTokens = baiduResponse.Usage.TotalTokens - baiduResponse.Usage.PromptTokens
|
||||||
|
}
|
||||||
|
response := streamResponseBaidu2OpenAI(&baiduResponse)
|
||||||
|
err = render.ObjectData(c, response)
|
||||||
|
if err != nil {
|
||||||
|
logger.SysError(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
logger.SysError("error reading stream: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Done(c)
|
||||||
|
|
||||||
err := resp.Body.Close()
|
err := resp.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil
|
return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ package cloudflare
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"github.com/songquanpeng/one-api/common/render"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -17,21 +17,20 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func ConvertRequest(textRequest model.GeneralOpenAIRequest) *Request {
|
func ConvertRequest(textRequest model.GeneralOpenAIRequest) *Request {
|
||||||
var promptBuilder strings.Builder
|
var promptBuilder strings.Builder
|
||||||
for _, message := range textRequest.Messages {
|
for _, message := range textRequest.Messages {
|
||||||
promptBuilder.WriteString(message.StringContent())
|
promptBuilder.WriteString(message.StringContent())
|
||||||
promptBuilder.WriteString("\n") // 添加换行符来分隔每个消息
|
promptBuilder.WriteString("\n") // 添加换行符来分隔每个消息
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Request{
|
return &Request{
|
||||||
MaxTokens: textRequest.MaxTokens,
|
MaxTokens: textRequest.MaxTokens,
|
||||||
Prompt: promptBuilder.String(),
|
Prompt: promptBuilder.String(),
|
||||||
Stream: textRequest.Stream,
|
Stream: textRequest.Stream,
|
||||||
Temperature: textRequest.Temperature,
|
Temperature: textRequest.Temperature,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func ResponseCloudflare2OpenAI(cloudflareResponse *Response) *openai.TextResponse {
|
func ResponseCloudflare2OpenAI(cloudflareResponse *Response) *openai.TextResponse {
|
||||||
choice := openai.TextResponseChoice{
|
choice := openai.TextResponseChoice{
|
||||||
Index: 0,
|
Index: 0,
|
||||||
@@ -63,67 +62,54 @@ func StreamResponseCloudflare2OpenAI(cloudflareResponse *StreamResponse) *openai
|
|||||||
|
|
||||||
func StreamHandler(c *gin.Context, resp *http.Response, promptTokens int, modelName string) (*model.ErrorWithStatusCode, *model.Usage) {
|
func StreamHandler(c *gin.Context, resp *http.Response, promptTokens int, modelName string) (*model.ErrorWithStatusCode, *model.Usage) {
|
||||||
scanner := bufio.NewScanner(resp.Body)
|
scanner := bufio.NewScanner(resp.Body)
|
||||||
scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
scanner.Split(bufio.ScanLines)
|
||||||
if atEOF && len(data) == 0 {
|
|
||||||
return 0, nil, nil
|
|
||||||
}
|
|
||||||
if i := bytes.IndexByte(data, '\n'); i >= 0 {
|
|
||||||
return i + 1, data[0:i], nil
|
|
||||||
}
|
|
||||||
if atEOF {
|
|
||||||
return len(data), data, nil
|
|
||||||
}
|
|
||||||
return 0, nil, nil
|
|
||||||
})
|
|
||||||
|
|
||||||
dataChan := make(chan string)
|
|
||||||
stopChan := make(chan bool)
|
|
||||||
go func() {
|
|
||||||
for scanner.Scan() {
|
|
||||||
data := scanner.Text()
|
|
||||||
if len(data) < len("data: ") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
data = strings.TrimPrefix(data, "data: ")
|
|
||||||
dataChan <- data
|
|
||||||
}
|
|
||||||
stopChan <- true
|
|
||||||
}()
|
|
||||||
common.SetEventStreamHeaders(c)
|
common.SetEventStreamHeaders(c)
|
||||||
id := helper.GetResponseID(c)
|
id := helper.GetResponseID(c)
|
||||||
responseModel := c.GetString("original_model")
|
responseModel := c.GetString("original_model")
|
||||||
var responseText string
|
var responseText string
|
||||||
c.Stream(func(w io.Writer) bool {
|
|
||||||
select {
|
for scanner.Scan() {
|
||||||
case data := <-dataChan:
|
data := scanner.Text()
|
||||||
// some implementations may add \r at the end of data
|
if len(data) < len("data: ") {
|
||||||
data = strings.TrimSuffix(data, "\r")
|
continue
|
||||||
var cloudflareResponse StreamResponse
|
|
||||||
err := json.Unmarshal([]byte(data), &cloudflareResponse)
|
|
||||||
if err != nil {
|
|
||||||
logger.SysError("error unmarshalling stream response: " + err.Error())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
response := StreamResponseCloudflare2OpenAI(&cloudflareResponse)
|
|
||||||
if response == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
responseText += cloudflareResponse.Response
|
|
||||||
response.Id = id
|
|
||||||
response.Model = responseModel
|
|
||||||
jsonStr, err := json.Marshal(response)
|
|
||||||
if err != nil {
|
|
||||||
logger.SysError("error marshalling stream response: " + err.Error())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
c.Render(-1, common.CustomEvent{Data: "data: " + string(jsonStr)})
|
|
||||||
return true
|
|
||||||
case <-stopChan:
|
|
||||||
c.Render(-1, common.CustomEvent{Data: "data: [DONE]"})
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
})
|
data = strings.TrimPrefix(data, "data: ")
|
||||||
_ = resp.Body.Close()
|
data = strings.TrimSuffix(data, "\r")
|
||||||
|
|
||||||
|
var cloudflareResponse StreamResponse
|
||||||
|
err := json.Unmarshal([]byte(data), &cloudflareResponse)
|
||||||
|
if err != nil {
|
||||||
|
logger.SysError("error unmarshalling stream response: " + err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
response := StreamResponseCloudflare2OpenAI(&cloudflareResponse)
|
||||||
|
if response == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
responseText += cloudflareResponse.Response
|
||||||
|
response.Id = id
|
||||||
|
response.Model = responseModel
|
||||||
|
|
||||||
|
err = render.ObjectData(c, response)
|
||||||
|
if err != nil {
|
||||||
|
logger.SysError(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
logger.SysError("error reading stream: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Done(c)
|
||||||
|
|
||||||
|
err := resp.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil
|
||||||
|
}
|
||||||
|
|
||||||
usage := openai.ResponseText2Usage(responseText, responseModel, promptTokens)
|
usage := openai.ResponseText2Usage(responseText, responseModel, promptTokens)
|
||||||
return nil, usage
|
return nil, usage
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ package cohere
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/songquanpeng/one-api/common/render"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -134,66 +134,53 @@ func ResponseCohere2OpenAI(cohereResponse *Response) *openai.TextResponse {
|
|||||||
func StreamHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusCode, *model.Usage) {
|
func StreamHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusCode, *model.Usage) {
|
||||||
createdTime := helper.GetTimestamp()
|
createdTime := helper.GetTimestamp()
|
||||||
scanner := bufio.NewScanner(resp.Body)
|
scanner := bufio.NewScanner(resp.Body)
|
||||||
scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
scanner.Split(bufio.ScanLines)
|
||||||
if atEOF && len(data) == 0 {
|
|
||||||
return 0, nil, nil
|
|
||||||
}
|
|
||||||
if i := bytes.IndexByte(data, '\n'); i >= 0 {
|
|
||||||
return i + 1, data[0:i], nil
|
|
||||||
}
|
|
||||||
if atEOF {
|
|
||||||
return len(data), data, nil
|
|
||||||
}
|
|
||||||
return 0, nil, nil
|
|
||||||
})
|
|
||||||
|
|
||||||
dataChan := make(chan string)
|
|
||||||
stopChan := make(chan bool)
|
|
||||||
go func() {
|
|
||||||
for scanner.Scan() {
|
|
||||||
data := scanner.Text()
|
|
||||||
dataChan <- data
|
|
||||||
}
|
|
||||||
stopChan <- true
|
|
||||||
}()
|
|
||||||
common.SetEventStreamHeaders(c)
|
common.SetEventStreamHeaders(c)
|
||||||
var usage model.Usage
|
var usage model.Usage
|
||||||
c.Stream(func(w io.Writer) bool {
|
|
||||||
select {
|
for scanner.Scan() {
|
||||||
case data := <-dataChan:
|
data := scanner.Text()
|
||||||
// some implementations may add \r at the end of data
|
data = strings.TrimSuffix(data, "\r")
|
||||||
data = strings.TrimSuffix(data, "\r")
|
|
||||||
var cohereResponse StreamResponse
|
var cohereResponse StreamResponse
|
||||||
err := json.Unmarshal([]byte(data), &cohereResponse)
|
err := json.Unmarshal([]byte(data), &cohereResponse)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.SysError("error unmarshalling stream response: " + err.Error())
|
logger.SysError("error unmarshalling stream response: " + err.Error())
|
||||||
return true
|
continue
|
||||||
}
|
|
||||||
response, meta := StreamResponseCohere2OpenAI(&cohereResponse)
|
|
||||||
if meta != nil {
|
|
||||||
usage.PromptTokens += meta.Meta.Tokens.InputTokens
|
|
||||||
usage.CompletionTokens += meta.Meta.Tokens.OutputTokens
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if response == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
response.Id = fmt.Sprintf("chatcmpl-%d", createdTime)
|
|
||||||
response.Model = c.GetString("original_model")
|
|
||||||
response.Created = createdTime
|
|
||||||
jsonStr, err := json.Marshal(response)
|
|
||||||
if err != nil {
|
|
||||||
logger.SysError("error marshalling stream response: " + err.Error())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
c.Render(-1, common.CustomEvent{Data: "data: " + string(jsonStr)})
|
|
||||||
return true
|
|
||||||
case <-stopChan:
|
|
||||||
c.Render(-1, common.CustomEvent{Data: "data: [DONE]"})
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
_ = resp.Body.Close()
|
response, meta := StreamResponseCohere2OpenAI(&cohereResponse)
|
||||||
|
if meta != nil {
|
||||||
|
usage.PromptTokens += meta.Meta.Tokens.InputTokens
|
||||||
|
usage.CompletionTokens += meta.Meta.Tokens.OutputTokens
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if response == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
response.Id = fmt.Sprintf("chatcmpl-%d", createdTime)
|
||||||
|
response.Model = c.GetString("original_model")
|
||||||
|
response.Created = createdTime
|
||||||
|
|
||||||
|
err = render.ObjectData(c, response)
|
||||||
|
if err != nil {
|
||||||
|
logger.SysError(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
logger.SysError("error reading stream: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Done(c)
|
||||||
|
|
||||||
|
err := resp.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil
|
||||||
|
}
|
||||||
|
|
||||||
return nil, &usage
|
return nil, &usage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,11 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/songquanpeng/one-api/common/render"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/songquanpeng/one-api/common"
|
"github.com/songquanpeng/one-api/common"
|
||||||
"github.com/songquanpeng/one-api/common/conv"
|
"github.com/songquanpeng/one-api/common/conv"
|
||||||
@@ -12,9 +17,6 @@ import (
|
|||||||
"github.com/songquanpeng/one-api/relay/adaptor/coze/constant/messagetype"
|
"github.com/songquanpeng/one-api/relay/adaptor/coze/constant/messagetype"
|
||||||
"github.com/songquanpeng/one-api/relay/adaptor/openai"
|
"github.com/songquanpeng/one-api/relay/adaptor/openai"
|
||||||
"github.com/songquanpeng/one-api/relay/model"
|
"github.com/songquanpeng/one-api/relay/model"
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// https://www.coze.com/open
|
// https://www.coze.com/open
|
||||||
@@ -109,69 +111,54 @@ func StreamHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusC
|
|||||||
var responseText string
|
var responseText string
|
||||||
createdTime := helper.GetTimestamp()
|
createdTime := helper.GetTimestamp()
|
||||||
scanner := bufio.NewScanner(resp.Body)
|
scanner := bufio.NewScanner(resp.Body)
|
||||||
scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
scanner.Split(bufio.ScanLines)
|
||||||
if atEOF && len(data) == 0 {
|
|
||||||
return 0, nil, nil
|
|
||||||
}
|
|
||||||
if i := strings.Index(string(data), "\n"); i >= 0 {
|
|
||||||
return i + 1, data[0:i], nil
|
|
||||||
}
|
|
||||||
if atEOF {
|
|
||||||
return len(data), data, nil
|
|
||||||
}
|
|
||||||
return 0, nil, nil
|
|
||||||
})
|
|
||||||
dataChan := make(chan string)
|
|
||||||
stopChan := make(chan bool)
|
|
||||||
go func() {
|
|
||||||
for scanner.Scan() {
|
|
||||||
data := scanner.Text()
|
|
||||||
if len(data) < 5 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !strings.HasPrefix(data, "data:") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
data = strings.TrimPrefix(data, "data:")
|
|
||||||
dataChan <- data
|
|
||||||
}
|
|
||||||
stopChan <- true
|
|
||||||
}()
|
|
||||||
common.SetEventStreamHeaders(c)
|
common.SetEventStreamHeaders(c)
|
||||||
var modelName string
|
var modelName string
|
||||||
c.Stream(func(w io.Writer) bool {
|
|
||||||
select {
|
for scanner.Scan() {
|
||||||
case data := <-dataChan:
|
data := scanner.Text()
|
||||||
// some implementations may add \r at the end of data
|
if len(data) < 5 || !strings.HasPrefix(data, "data:") {
|
||||||
data = strings.TrimSuffix(data, "\r")
|
continue
|
||||||
var cozeResponse StreamResponse
|
|
||||||
err := json.Unmarshal([]byte(data), &cozeResponse)
|
|
||||||
if err != nil {
|
|
||||||
logger.SysError("error unmarshalling stream response: " + err.Error())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
response, _ := StreamResponseCoze2OpenAI(&cozeResponse)
|
|
||||||
if response == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
for _, choice := range response.Choices {
|
|
||||||
responseText += conv.AsString(choice.Delta.Content)
|
|
||||||
}
|
|
||||||
response.Model = modelName
|
|
||||||
response.Created = createdTime
|
|
||||||
jsonStr, err := json.Marshal(response)
|
|
||||||
if err != nil {
|
|
||||||
logger.SysError("error marshalling stream response: " + err.Error())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
c.Render(-1, common.CustomEvent{Data: "data: " + string(jsonStr)})
|
|
||||||
return true
|
|
||||||
case <-stopChan:
|
|
||||||
c.Render(-1, common.CustomEvent{Data: "data: [DONE]"})
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
})
|
data = strings.TrimPrefix(data, "data:")
|
||||||
_ = resp.Body.Close()
|
data = strings.TrimSuffix(data, "\r")
|
||||||
|
|
||||||
|
var cozeResponse StreamResponse
|
||||||
|
err := json.Unmarshal([]byte(data), &cozeResponse)
|
||||||
|
if err != nil {
|
||||||
|
logger.SysError("error unmarshalling stream response: " + err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
response, _ := StreamResponseCoze2OpenAI(&cozeResponse)
|
||||||
|
if response == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, choice := range response.Choices {
|
||||||
|
responseText += conv.AsString(choice.Delta.Content)
|
||||||
|
}
|
||||||
|
response.Model = modelName
|
||||||
|
response.Created = createdTime
|
||||||
|
|
||||||
|
err = render.ObjectData(c, response)
|
||||||
|
if err != nil {
|
||||||
|
logger.SysError(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
logger.SysError("error reading stream: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Done(c)
|
||||||
|
|
||||||
|
err := resp.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil
|
||||||
|
}
|
||||||
|
|
||||||
return nil, &responseText
|
return nil, &responseText
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/songquanpeng/one-api/common/render"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -275,64 +276,50 @@ func embeddingResponseGemini2OpenAI(response *EmbeddingResponse) *openai.Embeddi
|
|||||||
func StreamHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusCode, string) {
|
func StreamHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusCode, string) {
|
||||||
responseText := ""
|
responseText := ""
|
||||||
scanner := bufio.NewScanner(resp.Body)
|
scanner := bufio.NewScanner(resp.Body)
|
||||||
scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
scanner.Split(bufio.ScanLines)
|
||||||
if atEOF && len(data) == 0 {
|
|
||||||
return 0, nil, nil
|
|
||||||
}
|
|
||||||
if i := strings.Index(string(data), "\n"); i >= 0 {
|
|
||||||
return i + 1, data[0:i], nil
|
|
||||||
}
|
|
||||||
if atEOF {
|
|
||||||
return len(data), data, nil
|
|
||||||
}
|
|
||||||
return 0, nil, nil
|
|
||||||
})
|
|
||||||
dataChan := make(chan string)
|
|
||||||
stopChan := make(chan bool)
|
|
||||||
go func() {
|
|
||||||
for scanner.Scan() {
|
|
||||||
data := scanner.Text()
|
|
||||||
data = strings.TrimSpace(data)
|
|
||||||
if !strings.HasPrefix(data, "data: ") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
data = strings.TrimPrefix(data, "data: ")
|
|
||||||
data = strings.TrimSuffix(data, "\"")
|
|
||||||
dataChan <- data
|
|
||||||
}
|
|
||||||
stopChan <- true
|
|
||||||
}()
|
|
||||||
common.SetEventStreamHeaders(c)
|
common.SetEventStreamHeaders(c)
|
||||||
c.Stream(func(w io.Writer) bool {
|
|
||||||
select {
|
for scanner.Scan() {
|
||||||
case data := <-dataChan:
|
data := scanner.Text()
|
||||||
var geminiResponse ChatResponse
|
data = strings.TrimSpace(data)
|
||||||
err := json.Unmarshal([]byte(data), &geminiResponse)
|
if !strings.HasPrefix(data, "data: ") {
|
||||||
if err != nil {
|
continue
|
||||||
logger.SysError("error unmarshalling stream response: " + err.Error())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
response := streamResponseGeminiChat2OpenAI(&geminiResponse)
|
|
||||||
if response == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
responseText += response.Choices[0].Delta.StringContent()
|
|
||||||
jsonResponse, err := json.Marshal(response)
|
|
||||||
if err != nil {
|
|
||||||
logger.SysError("error marshalling stream response: " + err.Error())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
c.Render(-1, common.CustomEvent{Data: "data: " + string(jsonResponse)})
|
|
||||||
return true
|
|
||||||
case <-stopChan:
|
|
||||||
c.Render(-1, common.CustomEvent{Data: "data: [DONE]"})
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
})
|
data = strings.TrimPrefix(data, "data: ")
|
||||||
|
data = strings.TrimSuffix(data, "\"")
|
||||||
|
|
||||||
|
var geminiResponse ChatResponse
|
||||||
|
err := json.Unmarshal([]byte(data), &geminiResponse)
|
||||||
|
if err != nil {
|
||||||
|
logger.SysError("error unmarshalling stream response: " + err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
response := streamResponseGeminiChat2OpenAI(&geminiResponse)
|
||||||
|
if response == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
responseText += response.Choices[0].Delta.StringContent()
|
||||||
|
|
||||||
|
err = render.ObjectData(c, response)
|
||||||
|
if err != nil {
|
||||||
|
logger.SysError(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
logger.SysError("error reading stream: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Done(c)
|
||||||
|
|
||||||
err := resp.Body.Close()
|
err := resp.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), ""
|
return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), ""
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, responseText
|
return nil, responseText
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,12 +5,14 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/songquanpeng/one-api/common/helper"
|
"github.com/songquanpeng/one-api/common/render"
|
||||||
"github.com/songquanpeng/one-api/common/random"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/songquanpeng/one-api/common/helper"
|
||||||
|
"github.com/songquanpeng/one-api/common/random"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/songquanpeng/one-api/common"
|
"github.com/songquanpeng/one-api/common"
|
||||||
"github.com/songquanpeng/one-api/common/image"
|
"github.com/songquanpeng/one-api/common/image"
|
||||||
@@ -105,54 +107,51 @@ func StreamHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusC
|
|||||||
return 0, nil, nil
|
return 0, nil, nil
|
||||||
}
|
}
|
||||||
if i := strings.Index(string(data), "}\n"); i >= 0 {
|
if i := strings.Index(string(data), "}\n"); i >= 0 {
|
||||||
return i + 2, data[0:i], nil
|
return i + 2, data[0 : i+1], nil
|
||||||
}
|
}
|
||||||
if atEOF {
|
if atEOF {
|
||||||
return len(data), data, nil
|
return len(data), data, nil
|
||||||
}
|
}
|
||||||
return 0, nil, nil
|
return 0, nil, nil
|
||||||
})
|
})
|
||||||
dataChan := make(chan string)
|
|
||||||
stopChan := make(chan bool)
|
|
||||||
go func() {
|
|
||||||
for scanner.Scan() {
|
|
||||||
data := strings.TrimPrefix(scanner.Text(), "}")
|
|
||||||
dataChan <- data + "}"
|
|
||||||
}
|
|
||||||
stopChan <- true
|
|
||||||
}()
|
|
||||||
common.SetEventStreamHeaders(c)
|
common.SetEventStreamHeaders(c)
|
||||||
c.Stream(func(w io.Writer) bool {
|
|
||||||
select {
|
for scanner.Scan() {
|
||||||
case data := <-dataChan:
|
data := strings.TrimPrefix(scanner.Text(), "}")
|
||||||
var ollamaResponse ChatResponse
|
data = data + "}"
|
||||||
err := json.Unmarshal([]byte(data), &ollamaResponse)
|
|
||||||
if err != nil {
|
var ollamaResponse ChatResponse
|
||||||
logger.SysError("error unmarshalling stream response: " + err.Error())
|
err := json.Unmarshal([]byte(data), &ollamaResponse)
|
||||||
return true
|
if err != nil {
|
||||||
}
|
logger.SysError("error unmarshalling stream response: " + err.Error())
|
||||||
if ollamaResponse.EvalCount != 0 {
|
continue
|
||||||
usage.PromptTokens = ollamaResponse.PromptEvalCount
|
|
||||||
usage.CompletionTokens = ollamaResponse.EvalCount
|
|
||||||
usage.TotalTokens = ollamaResponse.PromptEvalCount + ollamaResponse.EvalCount
|
|
||||||
}
|
|
||||||
response := streamResponseOllama2OpenAI(&ollamaResponse)
|
|
||||||
jsonResponse, err := json.Marshal(response)
|
|
||||||
if err != nil {
|
|
||||||
logger.SysError("error marshalling stream response: " + err.Error())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
c.Render(-1, common.CustomEvent{Data: "data: " + string(jsonResponse)})
|
|
||||||
return true
|
|
||||||
case <-stopChan:
|
|
||||||
c.Render(-1, common.CustomEvent{Data: "data: [DONE]"})
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
if ollamaResponse.EvalCount != 0 {
|
||||||
|
usage.PromptTokens = ollamaResponse.PromptEvalCount
|
||||||
|
usage.CompletionTokens = ollamaResponse.EvalCount
|
||||||
|
usage.TotalTokens = ollamaResponse.PromptEvalCount + ollamaResponse.EvalCount
|
||||||
|
}
|
||||||
|
|
||||||
|
response := streamResponseOllama2OpenAI(&ollamaResponse)
|
||||||
|
err = render.ObjectData(c, response)
|
||||||
|
if err != nil {
|
||||||
|
logger.SysError(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
logger.SysError("error reading stream: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Done(c)
|
||||||
|
|
||||||
err := resp.Body.Close()
|
err := resp.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil
|
return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, &usage
|
return nil, &usage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,15 +4,17 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"github.com/songquanpeng/one-api/common/render"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/songquanpeng/one-api/common"
|
"github.com/songquanpeng/one-api/common"
|
||||||
"github.com/songquanpeng/one-api/common/conv"
|
"github.com/songquanpeng/one-api/common/conv"
|
||||||
"github.com/songquanpeng/one-api/common/logger"
|
"github.com/songquanpeng/one-api/common/logger"
|
||||||
"github.com/songquanpeng/one-api/relay/model"
|
"github.com/songquanpeng/one-api/relay/model"
|
||||||
"github.com/songquanpeng/one-api/relay/relaymode"
|
"github.com/songquanpeng/one-api/relay/relaymode"
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -24,88 +26,68 @@ const (
|
|||||||
func StreamHandler(c *gin.Context, resp *http.Response, relayMode int) (*model.ErrorWithStatusCode, string, *model.Usage) {
|
func StreamHandler(c *gin.Context, resp *http.Response, relayMode int) (*model.ErrorWithStatusCode, string, *model.Usage) {
|
||||||
responseText := ""
|
responseText := ""
|
||||||
scanner := bufio.NewScanner(resp.Body)
|
scanner := bufio.NewScanner(resp.Body)
|
||||||
scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
scanner.Split(bufio.ScanLines)
|
||||||
if atEOF && len(data) == 0 {
|
|
||||||
return 0, nil, nil
|
|
||||||
}
|
|
||||||
if i := strings.Index(string(data), "\n"); i >= 0 {
|
|
||||||
return i + 1, data[0:i], nil
|
|
||||||
}
|
|
||||||
if atEOF {
|
|
||||||
return len(data), data, nil
|
|
||||||
}
|
|
||||||
return 0, nil, nil
|
|
||||||
})
|
|
||||||
dataChan := make(chan string)
|
|
||||||
stopChan := make(chan bool)
|
|
||||||
var usage *model.Usage
|
var usage *model.Usage
|
||||||
go func() {
|
|
||||||
for scanner.Scan() {
|
|
||||||
data := scanner.Text()
|
|
||||||
if len(data) < dataPrefixLength { // ignore blank line or wrong format
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if data[:dataPrefixLength] != dataPrefix && data[:dataPrefixLength] != done {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(data[dataPrefixLength:], done) {
|
|
||||||
dataChan <- data
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
switch relayMode {
|
|
||||||
case relaymode.ChatCompletions:
|
|
||||||
var streamResponse ChatCompletionsStreamResponse
|
|
||||||
err := json.Unmarshal([]byte(data[dataPrefixLength:]), &streamResponse)
|
|
||||||
if err != nil {
|
|
||||||
logger.SysError("error unmarshalling stream response: " + err.Error())
|
|
||||||
dataChan <- data // if error happened, pass the data to client
|
|
||||||
continue // just ignore the error
|
|
||||||
}
|
|
||||||
if len(streamResponse.Choices) == 0 {
|
|
||||||
// but for empty choice, we should not pass it to client, this is for azure
|
|
||||||
continue // just ignore empty choice
|
|
||||||
}
|
|
||||||
dataChan <- data
|
|
||||||
for _, choice := range streamResponse.Choices {
|
|
||||||
responseText += conv.AsString(choice.Delta.Content)
|
|
||||||
}
|
|
||||||
if streamResponse.Usage != nil {
|
|
||||||
usage = streamResponse.Usage
|
|
||||||
}
|
|
||||||
case relaymode.Completions:
|
|
||||||
dataChan <- data
|
|
||||||
var streamResponse CompletionsStreamResponse
|
|
||||||
err := json.Unmarshal([]byte(data[dataPrefixLength:]), &streamResponse)
|
|
||||||
if err != nil {
|
|
||||||
logger.SysError("error unmarshalling stream response: " + err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, choice := range streamResponse.Choices {
|
|
||||||
responseText += choice.Text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stopChan <- true
|
|
||||||
}()
|
|
||||||
common.SetEventStreamHeaders(c)
|
common.SetEventStreamHeaders(c)
|
||||||
c.Stream(func(w io.Writer) bool {
|
|
||||||
select {
|
for scanner.Scan() {
|
||||||
case data := <-dataChan:
|
data := scanner.Text()
|
||||||
if strings.HasPrefix(data, "data: [DONE]") {
|
if len(data) < dataPrefixLength { // ignore blank line or wrong format
|
||||||
data = data[:12]
|
continue
|
||||||
}
|
|
||||||
// some implementations may add \r at the end of data
|
|
||||||
data = strings.TrimSuffix(data, "\r")
|
|
||||||
c.Render(-1, common.CustomEvent{Data: data})
|
|
||||||
return true
|
|
||||||
case <-stopChan:
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
})
|
if data[:dataPrefixLength] != dataPrefix && data[:dataPrefixLength] != done {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(data[dataPrefixLength:], done) {
|
||||||
|
render.StringData(c, data)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch relayMode {
|
||||||
|
case relaymode.ChatCompletions:
|
||||||
|
var streamResponse ChatCompletionsStreamResponse
|
||||||
|
err := json.Unmarshal([]byte(data[dataPrefixLength:]), &streamResponse)
|
||||||
|
if err != nil {
|
||||||
|
logger.SysError("error unmarshalling stream response: " + err.Error())
|
||||||
|
render.StringData(c, data) // if error happened, pass the data to client
|
||||||
|
continue // just ignore the error
|
||||||
|
}
|
||||||
|
if len(streamResponse.Choices) == 0 {
|
||||||
|
// but for empty choice, we should not pass it to client, this is for azure
|
||||||
|
continue // just ignore empty choice
|
||||||
|
}
|
||||||
|
render.StringData(c, data)
|
||||||
|
for _, choice := range streamResponse.Choices {
|
||||||
|
responseText += conv.AsString(choice.Delta.Content)
|
||||||
|
}
|
||||||
|
if streamResponse.Usage != nil {
|
||||||
|
usage = streamResponse.Usage
|
||||||
|
}
|
||||||
|
case relaymode.Completions:
|
||||||
|
render.StringData(c, data)
|
||||||
|
var streamResponse CompletionsStreamResponse
|
||||||
|
err := json.Unmarshal([]byte(data[dataPrefixLength:]), &streamResponse)
|
||||||
|
if err != nil {
|
||||||
|
logger.SysError("error unmarshalling stream response: " + err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, choice := range streamResponse.Choices {
|
||||||
|
responseText += choice.Text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
logger.SysError("error reading stream: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Done(c)
|
||||||
|
|
||||||
err := resp.Body.Close()
|
err := resp.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), "", nil
|
return ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, responseText, usage
|
return nil, responseText, usage
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,7 +131,7 @@ func Handler(c *gin.Context, resp *http.Response, promptTokens int, modelName st
|
|||||||
return ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil
|
return ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if textResponse.Usage.TotalTokens == 0 {
|
if textResponse.Usage.TotalTokens == 0 || (textResponse.Usage.PromptTokens == 0 && textResponse.Usage.CompletionTokens == 0) {
|
||||||
completionTokens := 0
|
completionTokens := 0
|
||||||
for _, choice := range textResponse.Choices {
|
for _, choice := range textResponse.Choices {
|
||||||
completionTokens += CountTokenText(choice.Message.StringContent(), modelName)
|
completionTokens += CountTokenText(choice.Message.StringContent(), modelName)
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ package palm
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/songquanpeng/one-api/common/render"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/songquanpeng/one-api/common"
|
"github.com/songquanpeng/one-api/common"
|
||||||
"github.com/songquanpeng/one-api/common/helper"
|
"github.com/songquanpeng/one-api/common/helper"
|
||||||
@@ -11,8 +15,6 @@ import (
|
|||||||
"github.com/songquanpeng/one-api/relay/adaptor/openai"
|
"github.com/songquanpeng/one-api/relay/adaptor/openai"
|
||||||
"github.com/songquanpeng/one-api/relay/constant"
|
"github.com/songquanpeng/one-api/relay/constant"
|
||||||
"github.com/songquanpeng/one-api/relay/model"
|
"github.com/songquanpeng/one-api/relay/model"
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// https://developers.generativeai.google/api/rest/generativelanguage/models/generateMessage#request-body
|
// https://developers.generativeai.google/api/rest/generativelanguage/models/generateMessage#request-body
|
||||||
@@ -77,58 +79,51 @@ func StreamHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusC
|
|||||||
responseText := ""
|
responseText := ""
|
||||||
responseId := fmt.Sprintf("chatcmpl-%s", random.GetUUID())
|
responseId := fmt.Sprintf("chatcmpl-%s", random.GetUUID())
|
||||||
createdTime := helper.GetTimestamp()
|
createdTime := helper.GetTimestamp()
|
||||||
dataChan := make(chan string)
|
|
||||||
stopChan := make(chan bool)
|
|
||||||
go func() {
|
|
||||||
responseBody, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
logger.SysError("error reading stream response: " + err.Error())
|
|
||||||
stopChan <- true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = resp.Body.Close()
|
|
||||||
if err != nil {
|
|
||||||
logger.SysError("error closing stream response: " + err.Error())
|
|
||||||
stopChan <- true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var palmResponse ChatResponse
|
|
||||||
err = json.Unmarshal(responseBody, &palmResponse)
|
|
||||||
if err != nil {
|
|
||||||
logger.SysError("error unmarshalling stream response: " + err.Error())
|
|
||||||
stopChan <- true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fullTextResponse := streamResponsePaLM2OpenAI(&palmResponse)
|
|
||||||
fullTextResponse.Id = responseId
|
|
||||||
fullTextResponse.Created = createdTime
|
|
||||||
if len(palmResponse.Candidates) > 0 {
|
|
||||||
responseText = palmResponse.Candidates[0].Content
|
|
||||||
}
|
|
||||||
jsonResponse, err := json.Marshal(fullTextResponse)
|
|
||||||
if err != nil {
|
|
||||||
logger.SysError("error marshalling stream response: " + err.Error())
|
|
||||||
stopChan <- true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
dataChan <- string(jsonResponse)
|
|
||||||
stopChan <- true
|
|
||||||
}()
|
|
||||||
common.SetEventStreamHeaders(c)
|
common.SetEventStreamHeaders(c)
|
||||||
c.Stream(func(w io.Writer) bool {
|
|
||||||
select {
|
responseBody, err := io.ReadAll(resp.Body)
|
||||||
case data := <-dataChan:
|
if err != nil {
|
||||||
c.Render(-1, common.CustomEvent{Data: "data: " + data})
|
logger.SysError("error reading stream response: " + err.Error())
|
||||||
return true
|
err := resp.Body.Close()
|
||||||
case <-stopChan:
|
if err != nil {
|
||||||
c.Render(-1, common.CustomEvent{Data: "data: [DONE]"})
|
return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), ""
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
})
|
return openai.ErrorWrapper(err, "read_response_body_failed", http.StatusInternalServerError), ""
|
||||||
err := resp.Body.Close()
|
}
|
||||||
|
|
||||||
|
err = resp.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), ""
|
return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var palmResponse ChatResponse
|
||||||
|
err = json.Unmarshal(responseBody, &palmResponse)
|
||||||
|
if err != nil {
|
||||||
|
logger.SysError("error unmarshalling stream response: " + err.Error())
|
||||||
|
return openai.ErrorWrapper(err, "unmarshal_response_body_failed", http.StatusInternalServerError), ""
|
||||||
|
}
|
||||||
|
|
||||||
|
fullTextResponse := streamResponsePaLM2OpenAI(&palmResponse)
|
||||||
|
fullTextResponse.Id = responseId
|
||||||
|
fullTextResponse.Created = createdTime
|
||||||
|
if len(palmResponse.Candidates) > 0 {
|
||||||
|
responseText = palmResponse.Candidates[0].Content
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonResponse, err := json.Marshal(fullTextResponse)
|
||||||
|
if err != nil {
|
||||||
|
logger.SysError("error marshalling stream response: " + err.Error())
|
||||||
|
return openai.ErrorWrapper(err, "marshal_response_body_failed", http.StatusInternalServerError), ""
|
||||||
|
}
|
||||||
|
|
||||||
|
err = render.ObjectData(c, string(jsonResponse))
|
||||||
|
if err != nil {
|
||||||
|
logger.SysError(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Done(c)
|
||||||
|
|
||||||
return nil, responseText
|
return nil, responseText
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,13 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/songquanpeng/one-api/common/render"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/songquanpeng/one-api/common"
|
"github.com/songquanpeng/one-api/common"
|
||||||
"github.com/songquanpeng/one-api/common/conv"
|
"github.com/songquanpeng/one-api/common/conv"
|
||||||
@@ -17,11 +24,6 @@ import (
|
|||||||
"github.com/songquanpeng/one-api/relay/adaptor/openai"
|
"github.com/songquanpeng/one-api/relay/adaptor/openai"
|
||||||
"github.com/songquanpeng/one-api/relay/constant"
|
"github.com/songquanpeng/one-api/relay/constant"
|
||||||
"github.com/songquanpeng/one-api/relay/model"
|
"github.com/songquanpeng/one-api/relay/model"
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ConvertRequest(request model.GeneralOpenAIRequest) *ChatRequest {
|
func ConvertRequest(request model.GeneralOpenAIRequest) *ChatRequest {
|
||||||
@@ -87,64 +89,46 @@ func streamResponseTencent2OpenAI(TencentResponse *ChatResponse) *openai.ChatCom
|
|||||||
func StreamHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusCode, string) {
|
func StreamHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusCode, string) {
|
||||||
var responseText string
|
var responseText string
|
||||||
scanner := bufio.NewScanner(resp.Body)
|
scanner := bufio.NewScanner(resp.Body)
|
||||||
scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
scanner.Split(bufio.ScanLines)
|
||||||
if atEOF && len(data) == 0 {
|
|
||||||
return 0, nil, nil
|
|
||||||
}
|
|
||||||
if i := strings.Index(string(data), "\n"); i >= 0 {
|
|
||||||
return i + 1, data[0:i], nil
|
|
||||||
}
|
|
||||||
if atEOF {
|
|
||||||
return len(data), data, nil
|
|
||||||
}
|
|
||||||
return 0, nil, nil
|
|
||||||
})
|
|
||||||
dataChan := make(chan string)
|
|
||||||
stopChan := make(chan bool)
|
|
||||||
go func() {
|
|
||||||
for scanner.Scan() {
|
|
||||||
data := scanner.Text()
|
|
||||||
if len(data) < 5 { // ignore blank line or wrong format
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if data[:5] != "data:" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
data = data[5:]
|
|
||||||
dataChan <- data
|
|
||||||
}
|
|
||||||
stopChan <- true
|
|
||||||
}()
|
|
||||||
common.SetEventStreamHeaders(c)
|
common.SetEventStreamHeaders(c)
|
||||||
c.Stream(func(w io.Writer) bool {
|
|
||||||
select {
|
for scanner.Scan() {
|
||||||
case data := <-dataChan:
|
data := scanner.Text()
|
||||||
var TencentResponse ChatResponse
|
if len(data) < 5 || !strings.HasPrefix(data, "data:") {
|
||||||
err := json.Unmarshal([]byte(data), &TencentResponse)
|
continue
|
||||||
if err != nil {
|
|
||||||
logger.SysError("error unmarshalling stream response: " + err.Error())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
response := streamResponseTencent2OpenAI(&TencentResponse)
|
|
||||||
if len(response.Choices) != 0 {
|
|
||||||
responseText += conv.AsString(response.Choices[0].Delta.Content)
|
|
||||||
}
|
|
||||||
jsonResponse, err := json.Marshal(response)
|
|
||||||
if err != nil {
|
|
||||||
logger.SysError("error marshalling stream response: " + err.Error())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
c.Render(-1, common.CustomEvent{Data: "data: " + string(jsonResponse)})
|
|
||||||
return true
|
|
||||||
case <-stopChan:
|
|
||||||
c.Render(-1, common.CustomEvent{Data: "data: [DONE]"})
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
})
|
data = strings.TrimPrefix(data, "data:")
|
||||||
|
|
||||||
|
var tencentResponse ChatResponse
|
||||||
|
err := json.Unmarshal([]byte(data), &tencentResponse)
|
||||||
|
if err != nil {
|
||||||
|
logger.SysError("error unmarshalling stream response: " + err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
response := streamResponseTencent2OpenAI(&tencentResponse)
|
||||||
|
if len(response.Choices) != 0 {
|
||||||
|
responseText += conv.AsString(response.Choices[0].Delta.Content)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = render.ObjectData(c, response)
|
||||||
|
if err != nil {
|
||||||
|
logger.SysError(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
logger.SysError("error reading stream: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Done(c)
|
||||||
|
|
||||||
err := resp.Body.Close()
|
err := resp.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), ""
|
return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), ""
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, responseText
|
return nil, responseText
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,13 @@ package zhipu
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"github.com/songquanpeng/one-api/common/render"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/golang-jwt/jwt"
|
"github.com/golang-jwt/jwt"
|
||||||
"github.com/songquanpeng/one-api/common"
|
"github.com/songquanpeng/one-api/common"
|
||||||
@@ -11,11 +18,6 @@ import (
|
|||||||
"github.com/songquanpeng/one-api/relay/adaptor/openai"
|
"github.com/songquanpeng/one-api/relay/adaptor/openai"
|
||||||
"github.com/songquanpeng/one-api/relay/constant"
|
"github.com/songquanpeng/one-api/relay/constant"
|
||||||
"github.com/songquanpeng/one-api/relay/model"
|
"github.com/songquanpeng/one-api/relay/model"
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// https://open.bigmodel.cn/doc/api#chatglm_std
|
// https://open.bigmodel.cn/doc/api#chatglm_std
|
||||||
@@ -155,66 +157,55 @@ func StreamHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusC
|
|||||||
}
|
}
|
||||||
return 0, nil, nil
|
return 0, nil, nil
|
||||||
})
|
})
|
||||||
dataChan := make(chan string)
|
|
||||||
metaChan := make(chan string)
|
common.SetEventStreamHeaders(c)
|
||||||
stopChan := make(chan bool)
|
|
||||||
go func() {
|
for scanner.Scan() {
|
||||||
for scanner.Scan() {
|
data := scanner.Text()
|
||||||
data := scanner.Text()
|
lines := strings.Split(data, "\n")
|
||||||
lines := strings.Split(data, "\n")
|
for i, line := range lines {
|
||||||
for i, line := range lines {
|
if len(line) < 5 {
|
||||||
if len(line) < 5 {
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(line, "data:") {
|
||||||
|
dataSegment := line[5:]
|
||||||
|
if i != len(lines)-1 {
|
||||||
|
dataSegment += "\n"
|
||||||
|
}
|
||||||
|
response := streamResponseZhipu2OpenAI(dataSegment)
|
||||||
|
err := render.ObjectData(c, response)
|
||||||
|
if err != nil {
|
||||||
|
logger.SysError("error marshalling stream response: " + err.Error())
|
||||||
|
}
|
||||||
|
} else if strings.HasPrefix(line, "meta:") {
|
||||||
|
metaSegment := line[5:]
|
||||||
|
var zhipuResponse StreamMetaResponse
|
||||||
|
err := json.Unmarshal([]byte(metaSegment), &zhipuResponse)
|
||||||
|
if err != nil {
|
||||||
|
logger.SysError("error unmarshalling stream response: " + err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if line[:5] == "data:" {
|
response, zhipuUsage := streamMetaResponseZhipu2OpenAI(&zhipuResponse)
|
||||||
dataChan <- line[5:]
|
err = render.ObjectData(c, response)
|
||||||
if i != len(lines)-1 {
|
if err != nil {
|
||||||
dataChan <- "\n"
|
logger.SysError("error marshalling stream response: " + err.Error())
|
||||||
}
|
|
||||||
} else if line[:5] == "meta:" {
|
|
||||||
metaChan <- line[5:]
|
|
||||||
}
|
}
|
||||||
|
usage = zhipuUsage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stopChan <- true
|
}
|
||||||
}()
|
|
||||||
common.SetEventStreamHeaders(c)
|
if err := scanner.Err(); err != nil {
|
||||||
c.Stream(func(w io.Writer) bool {
|
logger.SysError("error reading stream: " + err.Error())
|
||||||
select {
|
}
|
||||||
case data := <-dataChan:
|
|
||||||
response := streamResponseZhipu2OpenAI(data)
|
render.Done(c)
|
||||||
jsonResponse, err := json.Marshal(response)
|
|
||||||
if err != nil {
|
|
||||||
logger.SysError("error marshalling stream response: " + err.Error())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
c.Render(-1, common.CustomEvent{Data: "data: " + string(jsonResponse)})
|
|
||||||
return true
|
|
||||||
case data := <-metaChan:
|
|
||||||
var zhipuResponse StreamMetaResponse
|
|
||||||
err := json.Unmarshal([]byte(data), &zhipuResponse)
|
|
||||||
if err != nil {
|
|
||||||
logger.SysError("error unmarshalling stream response: " + err.Error())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
response, zhipuUsage := streamMetaResponseZhipu2OpenAI(&zhipuResponse)
|
|
||||||
jsonResponse, err := json.Marshal(response)
|
|
||||||
if err != nil {
|
|
||||||
logger.SysError("error marshalling stream response: " + err.Error())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
usage = zhipuUsage
|
|
||||||
c.Render(-1, common.CustomEvent{Data: "data: " + string(jsonResponse)})
|
|
||||||
return true
|
|
||||||
case <-stopChan:
|
|
||||||
c.Render(-1, common.CustomEvent{Data: "data: [DONE]"})
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
err := resp.Body.Close()
|
err := resp.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil
|
return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, usage
|
return nil, usage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -70,12 +70,13 @@ var ModelRatio = map[string]float64{
|
|||||||
"dall-e-2": 0.02 * USD, // $0.016 - $0.020 / image
|
"dall-e-2": 0.02 * USD, // $0.016 - $0.020 / image
|
||||||
"dall-e-3": 0.04 * USD, // $0.040 - $0.120 / image
|
"dall-e-3": 0.04 * USD, // $0.040 - $0.120 / image
|
||||||
// https://www.anthropic.com/api#pricing
|
// https://www.anthropic.com/api#pricing
|
||||||
"claude-instant-1.2": 0.8 / 1000 * USD,
|
"claude-instant-1.2": 0.8 / 1000 * USD,
|
||||||
"claude-2.0": 8.0 / 1000 * USD,
|
"claude-2.0": 8.0 / 1000 * USD,
|
||||||
"claude-2.1": 8.0 / 1000 * USD,
|
"claude-2.1": 8.0 / 1000 * USD,
|
||||||
"claude-3-haiku-20240307": 0.25 / 1000 * USD,
|
"claude-3-haiku-20240307": 0.25 / 1000 * USD,
|
||||||
"claude-3-sonnet-20240229": 3.0 / 1000 * USD,
|
"claude-3-sonnet-20240229": 3.0 / 1000 * USD,
|
||||||
"claude-3-opus-20240229": 15.0 / 1000 * USD,
|
"claude-3-5-sonnet-20240620": 3.0 / 1000 * USD,
|
||||||
|
"claude-3-opus-20240229": 15.0 / 1000 * USD,
|
||||||
// https://cloud.baidu.com/doc/WENXINWORKSHOP/s/hlrk4akp7
|
// https://cloud.baidu.com/doc/WENXINWORKSHOP/s/hlrk4akp7
|
||||||
"ERNIE-4.0-8K": 0.120 * RMB,
|
"ERNIE-4.0-8K": 0.120 * RMB,
|
||||||
"ERNIE-3.5-8K": 0.012 * RMB,
|
"ERNIE-3.5-8K": 0.012 * RMB,
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ const EditChannel = (props) => {
|
|||||||
let localModels = [];
|
let localModels = [];
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case 14:
|
case 14:
|
||||||
localModels = ["claude-instant-1.2", "claude-2", "claude-2.0", "claude-2.1", "claude-3-opus-20240229", "claude-3-sonnet-20240229", "claude-3-haiku-20240307"];
|
localModels = ["claude-instant-1.2", "claude-2", "claude-2.0", "claude-2.1", "claude-3-opus-20240229", "claude-3-sonnet-20240229", "claude-3-haiku-20240307", "claude-3-5-sonnet-20240620"];
|
||||||
break;
|
break;
|
||||||
case 11:
|
case 11:
|
||||||
localModels = ['PaLM-2'];
|
localModels = ['PaLM-2'];
|
||||||
|
|||||||
Reference in New Issue
Block a user