✨ feat: xunfei support functions(stream)
This commit is contained in:
parent
e052009eba
commit
2810a96fd9
@ -14,6 +14,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var StopFinishReason = "stop"
|
var StopFinishReason = "stop"
|
||||||
|
var StopFinishReasonToolFunction = "tool_calls"
|
||||||
|
var StopFinishReasonCallFunction = "function_call"
|
||||||
|
|
||||||
type BaseProvider struct {
|
type BaseProvider struct {
|
||||||
BaseURL string
|
BaseURL string
|
||||||
@ -27,7 +29,6 @@ type BaseProvider struct {
|
|||||||
ImagesGenerations string
|
ImagesGenerations string
|
||||||
ImagesEdit string
|
ImagesEdit string
|
||||||
ImagesVariations string
|
ImagesVariations string
|
||||||
Proxy string
|
|
||||||
Context *gin.Context
|
Context *gin.Context
|
||||||
Channel *model.Channel
|
Channel *model.Channel
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package xunfei
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"one-api/common"
|
"one-api/common"
|
||||||
@ -15,22 +16,24 @@ import (
|
|||||||
func (p *XunfeiProvider) ChatAction(request *types.ChatCompletionRequest, isModelMapped bool, promptTokens int) (usage *types.Usage, errWithCode *types.OpenAIErrorWithStatusCode) {
|
func (p *XunfeiProvider) ChatAction(request *types.ChatCompletionRequest, isModelMapped bool, promptTokens int) (usage *types.Usage, errWithCode *types.OpenAIErrorWithStatusCode) {
|
||||||
authUrl := p.GetFullRequestURL(p.ChatCompletions, request.Model)
|
authUrl := p.GetFullRequestURL(p.ChatCompletions, request.Model)
|
||||||
|
|
||||||
if request.Stream {
|
|
||||||
return p.sendStreamRequest(request, authUrl)
|
|
||||||
} else {
|
|
||||||
return p.sendRequest(request, authUrl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *XunfeiProvider) sendRequest(request *types.ChatCompletionRequest, authUrl string) (usage *types.Usage, errWithCode *types.OpenAIErrorWithStatusCode) {
|
|
||||||
usage = &types.Usage{}
|
|
||||||
dataChan, stopChan, err := p.xunfeiMakeRequest(request, authUrl)
|
dataChan, stopChan, err := p.xunfeiMakeRequest(request, authUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, common.ErrorWrapper(err, "make xunfei request err", http.StatusInternalServerError)
|
return nil, common.ErrorWrapper(err, "make xunfei request err", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if request.Stream {
|
||||||
|
return p.sendStreamRequest(dataChan, stopChan, request.GetFunctionCate())
|
||||||
|
} else {
|
||||||
|
return p.sendRequest(dataChan, stopChan, request.GetFunctionCate())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *XunfeiProvider) sendRequest(dataChan chan XunfeiChatResponse, stopChan chan bool, functionCate string) (usage *types.Usage, errWithCode *types.OpenAIErrorWithStatusCode) {
|
||||||
|
usage = &types.Usage{}
|
||||||
|
|
||||||
var content string
|
var content string
|
||||||
var xunfeiResponse XunfeiChatResponse
|
var xunfeiResponse XunfeiChatResponse
|
||||||
|
|
||||||
stop := false
|
stop := false
|
||||||
for !stop {
|
for !stop {
|
||||||
select {
|
select {
|
||||||
@ -46,17 +49,17 @@ func (p *XunfeiProvider) sendRequest(request *types.ChatCompletionRequest, authU
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if xunfeiResponse.Header.Code != 0 {
|
||||||
|
return nil, common.ErrorWrapper(fmt.Errorf("xunfei response: %s", xunfeiResponse.Header.Message), "xunfei_response_error", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
if len(xunfeiResponse.Payload.Choices.Text) == 0 {
|
if len(xunfeiResponse.Payload.Choices.Text) == 0 {
|
||||||
xunfeiResponse.Payload.Choices.Text = []XunfeiChatResponseTextItem{
|
xunfeiResponse.Payload.Choices.Text = []XunfeiChatResponseTextItem{{}}
|
||||||
{
|
|
||||||
Content: "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
xunfeiResponse.Payload.Choices.Text[0].Content = content
|
xunfeiResponse.Payload.Choices.Text[0].Content = content
|
||||||
|
|
||||||
response := p.responseXunfei2OpenAI(&xunfeiResponse)
|
response := p.responseXunfei2OpenAI(&xunfeiResponse, functionCate)
|
||||||
jsonResponse, err := json.Marshal(response)
|
jsonResponse, err := json.Marshal(response)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, common.ErrorWrapper(err, "marshal_response_body_failed", http.StatusInternalServerError)
|
return nil, common.ErrorWrapper(err, "marshal_response_body_failed", http.StatusInternalServerError)
|
||||||
@ -66,30 +69,56 @@ func (p *XunfeiProvider) sendRequest(request *types.ChatCompletionRequest, authU
|
|||||||
return usage, nil
|
return usage, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *XunfeiProvider) sendStreamRequest(request *types.ChatCompletionRequest, authUrl string) (usage *types.Usage, errWithCode *types.OpenAIErrorWithStatusCode) {
|
func (p *XunfeiProvider) sendStreamRequest(dataChan chan XunfeiChatResponse, stopChan chan bool, functionCate string) (usage *types.Usage, errWithCode *types.OpenAIErrorWithStatusCode) {
|
||||||
usage = &types.Usage{}
|
usage = &types.Usage{}
|
||||||
dataChan, stopChan, err := p.xunfeiMakeRequest(request, authUrl)
|
|
||||||
if err != nil {
|
// 等待第一个dataChan的响应
|
||||||
return nil, common.ErrorWrapper(err, "make xunfei request err", http.StatusInternalServerError)
|
xunfeiResponse, ok := <-dataChan
|
||||||
|
if !ok {
|
||||||
|
return nil, common.ErrorWrapper(fmt.Errorf("xunfei response channel closed"), "xunfei_response_error", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
if xunfeiResponse.Header.Code != 0 {
|
||||||
|
errWithCode = common.ErrorWrapper(fmt.Errorf("xunfei response: %s", xunfeiResponse.Header.Message), "xunfei_response_error", http.StatusInternalServerError)
|
||||||
|
return nil, errWithCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果第一个响应没有错误,设置StreamHeaders并开始streaming
|
||||||
common.SetEventStreamHeaders(p.Context)
|
common.SetEventStreamHeaders(p.Context)
|
||||||
p.Context.Stream(func(w io.Writer) bool {
|
p.Context.Stream(func(w io.Writer) bool {
|
||||||
select {
|
// 处理第一个响应
|
||||||
case xunfeiResponse := <-dataChan:
|
usage.PromptTokens += xunfeiResponse.Payload.Usage.Text.PromptTokens
|
||||||
usage.PromptTokens += xunfeiResponse.Payload.Usage.Text.PromptTokens
|
usage.CompletionTokens += xunfeiResponse.Payload.Usage.Text.CompletionTokens
|
||||||
usage.CompletionTokens += xunfeiResponse.Payload.Usage.Text.CompletionTokens
|
usage.TotalTokens += xunfeiResponse.Payload.Usage.Text.TotalTokens
|
||||||
usage.TotalTokens += xunfeiResponse.Payload.Usage.Text.TotalTokens
|
response := p.streamResponseXunfei2OpenAI(&xunfeiResponse, functionCate)
|
||||||
response := p.streamResponseXunfei2OpenAI(&xunfeiResponse)
|
jsonResponse, err := json.Marshal(response)
|
||||||
jsonResponse, err := json.Marshal(response)
|
if err != nil {
|
||||||
if err != nil {
|
common.SysError("error marshalling stream response: " + err.Error())
|
||||||
common.SysError("error marshalling stream response: " + err.Error())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
p.Context.Render(-1, common.CustomEvent{Data: "data: " + string(jsonResponse)})
|
|
||||||
return true
|
return true
|
||||||
case <-stopChan:
|
}
|
||||||
p.Context.Render(-1, common.CustomEvent{Data: "data: [DONE]"})
|
p.Context.Render(-1, common.CustomEvent{Data: "data: " + string(jsonResponse)})
|
||||||
return false
|
|
||||||
|
// 处理后续的响应
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case xunfeiResponse, ok := <-dataChan:
|
||||||
|
if !ok {
|
||||||
|
p.Context.Render(-1, common.CustomEvent{Data: "data: [DONE]"})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
usage.PromptTokens += xunfeiResponse.Payload.Usage.Text.PromptTokens
|
||||||
|
usage.CompletionTokens += xunfeiResponse.Payload.Usage.Text.CompletionTokens
|
||||||
|
usage.TotalTokens += xunfeiResponse.Payload.Usage.Text.TotalTokens
|
||||||
|
response := p.streamResponseXunfei2OpenAI(&xunfeiResponse, functionCate)
|
||||||
|
jsonResponse, err := json.Marshal(response)
|
||||||
|
if err != nil {
|
||||||
|
common.SysError("error marshalling stream response: " + err.Error())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
p.Context.Render(-1, common.CustomEvent{Data: "data: " + string(jsonResponse)})
|
||||||
|
case <-stopChan:
|
||||||
|
p.Context.Render(-1, common.CustomEvent{Data: "data: [DONE]"})
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return usage, nil
|
return usage, nil
|
||||||
@ -123,6 +152,9 @@ func (p *XunfeiProvider) requestOpenAI2Xunfei(request *types.ChatCompletionReque
|
|||||||
}
|
}
|
||||||
xunfeiRequest.Payload.Functions = &XunfeiChatPayloadFunctions{}
|
xunfeiRequest.Payload.Functions = &XunfeiChatPayloadFunctions{}
|
||||||
xunfeiRequest.Payload.Functions.Text = functions
|
xunfeiRequest.Payload.Functions.Text = functions
|
||||||
|
} else if request.Functions != nil {
|
||||||
|
xunfeiRequest.Payload.Functions = &XunfeiChatPayloadFunctions{}
|
||||||
|
xunfeiRequest.Payload.Functions.Text = request.Functions
|
||||||
}
|
}
|
||||||
|
|
||||||
xunfeiRequest.Header.AppId = p.apiId
|
xunfeiRequest.Header.AppId = p.apiId
|
||||||
@ -134,13 +166,9 @@ func (p *XunfeiProvider) requestOpenAI2Xunfei(request *types.ChatCompletionReque
|
|||||||
return &xunfeiRequest
|
return &xunfeiRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *XunfeiProvider) responseXunfei2OpenAI(response *XunfeiChatResponse) *types.ChatCompletionResponse {
|
func (p *XunfeiProvider) responseXunfei2OpenAI(response *XunfeiChatResponse, functionCate string) *types.ChatCompletionResponse {
|
||||||
if len(response.Payload.Choices.Text) == 0 {
|
if len(response.Payload.Choices.Text) == 0 {
|
||||||
response.Payload.Choices.Text = []XunfeiChatResponseTextItem{
|
response.Payload.Choices.Text = []XunfeiChatResponseTextItem{{}}
|
||||||
{
|
|
||||||
Content: "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
choice := types.ChatCompletionChoice{
|
choice := types.ChatCompletionChoice{
|
||||||
@ -153,13 +181,22 @@ func (p *XunfeiProvider) responseXunfei2OpenAI(response *XunfeiChatResponse) *ty
|
|||||||
if xunfeiText.FunctionCall != nil {
|
if xunfeiText.FunctionCall != nil {
|
||||||
choice.Message = types.ChatCompletionMessage{
|
choice.Message = types.ChatCompletionMessage{
|
||||||
Role: "assistant",
|
Role: "assistant",
|
||||||
ToolCalls: []*types.ChatCompletionToolCalls{
|
}
|
||||||
|
|
||||||
|
if functionCate == "tool" {
|
||||||
|
choice.Message.ToolCalls = []*types.ChatCompletionToolCalls{
|
||||||
{
|
{
|
||||||
|
Id: response.Header.Sid,
|
||||||
Type: "function",
|
Type: "function",
|
||||||
Function: *xunfeiText.FunctionCall,
|
Function: *xunfeiText.FunctionCall,
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
|
choice.FinishReason = &base.StopFinishReasonToolFunction
|
||||||
|
} else {
|
||||||
|
choice.Message.FunctionCall = xunfeiText.FunctionCall
|
||||||
|
choice.FinishReason = &base.StopFinishReasonCallFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
choice.Message = types.ChatCompletionMessage{
|
choice.Message = types.ChatCompletionMessage{
|
||||||
Role: "assistant",
|
Role: "assistant",
|
||||||
@ -168,7 +205,9 @@ func (p *XunfeiProvider) responseXunfei2OpenAI(response *XunfeiChatResponse) *ty
|
|||||||
}
|
}
|
||||||
|
|
||||||
fullTextResponse := types.ChatCompletionResponse{
|
fullTextResponse := types.ChatCompletionResponse{
|
||||||
|
ID: response.Header.Sid,
|
||||||
Object: "chat.completion",
|
Object: "chat.completion",
|
||||||
|
Model: "SparkDesk",
|
||||||
Created: common.GetTimestamp(),
|
Created: common.GetTimestamp(),
|
||||||
Choices: []types.ChatCompletionChoice{choice},
|
Choices: []types.ChatCompletionChoice{choice},
|
||||||
Usage: &response.Payload.Usage.Text,
|
Usage: &response.Payload.Usage.Text,
|
||||||
@ -220,20 +259,38 @@ func (p *XunfeiProvider) xunfeiMakeRequest(textRequest *types.ChatCompletionRequ
|
|||||||
return dataChan, stopChan, nil
|
return dataChan, stopChan, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *XunfeiProvider) streamResponseXunfei2OpenAI(xunfeiResponse *XunfeiChatResponse) *types.ChatCompletionStreamResponse {
|
func (p *XunfeiProvider) streamResponseXunfei2OpenAI(xunfeiResponse *XunfeiChatResponse, functionCate string) *types.ChatCompletionStreamResponse {
|
||||||
if len(xunfeiResponse.Payload.Choices.Text) == 0 {
|
if len(xunfeiResponse.Payload.Choices.Text) == 0 {
|
||||||
xunfeiResponse.Payload.Choices.Text = []XunfeiChatResponseTextItem{
|
xunfeiResponse.Payload.Choices.Text = []XunfeiChatResponseTextItem{{}}
|
||||||
{
|
|
||||||
Content: "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
var choice types.ChatCompletionStreamChoice
|
var choice types.ChatCompletionStreamChoice
|
||||||
choice.Delta.Content = xunfeiResponse.Payload.Choices.Text[0].Content
|
xunfeiText := xunfeiResponse.Payload.Choices.Text[0]
|
||||||
if xunfeiResponse.Payload.Choices.Status == 2 {
|
|
||||||
choice.FinishReason = &base.StopFinishReason
|
if xunfeiText.FunctionCall != nil {
|
||||||
|
if functionCate == "tool" {
|
||||||
|
choice.Delta.ToolCalls = []*types.ChatCompletionToolCalls{
|
||||||
|
{
|
||||||
|
Id: xunfeiResponse.Header.Sid,
|
||||||
|
Index: 0,
|
||||||
|
Type: "function",
|
||||||
|
Function: *xunfeiText.FunctionCall,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
choice.FinishReason = &base.StopFinishReasonToolFunction
|
||||||
|
} else {
|
||||||
|
choice.Delta.FunctionCall = xunfeiText.FunctionCall
|
||||||
|
choice.FinishReason = &base.StopFinishReasonCallFunction
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
choice.Delta.Content = xunfeiResponse.Payload.Choices.Text[0].Content
|
||||||
|
if xunfeiResponse.Payload.Choices.Status == 2 {
|
||||||
|
choice.FinishReason = &base.StopFinishReason
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
response := types.ChatCompletionStreamResponse{
|
response := types.ChatCompletionStreamResponse{
|
||||||
|
ID: xunfeiResponse.Header.Sid,
|
||||||
Object: "chat.completion.chunk",
|
Object: "chat.completion.chunk",
|
||||||
Created: common.GetTimestamp(),
|
Created: common.GetTimestamp(),
|
||||||
Model: "SparkDesk",
|
Model: "SparkDesk",
|
||||||
|
@ -62,12 +62,6 @@ type XunfeiChatResponse struct {
|
|||||||
Text []XunfeiChatResponseTextItem `json:"text"`
|
Text []XunfeiChatResponseTextItem `json:"text"`
|
||||||
} `json:"choices"`
|
} `json:"choices"`
|
||||||
Usage struct {
|
Usage struct {
|
||||||
//Text struct {
|
|
||||||
// QuestionTokens string `json:"question_tokens"`
|
|
||||||
// PromptTokens string `json:"prompt_tokens"`
|
|
||||||
// CompletionTokens string `json:"completion_tokens"`
|
|
||||||
// TotalTokens string `json:"total_tokens"`
|
|
||||||
//} `json:"text"`
|
|
||||||
Text types.Usage `json:"text"`
|
Text types.Usage `json:"text"`
|
||||||
} `json:"usage"`
|
} `json:"usage"`
|
||||||
} `json:"payload"`
|
} `json:"payload"`
|
||||||
|
@ -6,12 +6,13 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ChatCompletionToolCallsFunction struct {
|
type ChatCompletionToolCallsFunction struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name,omitempty"`
|
||||||
Arguments string `json:"arguments"`
|
Arguments string `json:"arguments,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChatCompletionToolCalls struct {
|
type ChatCompletionToolCalls struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
|
Index int `json:"index,omitempty"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Function ChatCompletionToolCallsFunction `json:"function"`
|
Function ChatCompletionToolCallsFunction `json:"function"`
|
||||||
}
|
}
|
||||||
@ -129,6 +130,15 @@ type ChatCompletionRequest struct {
|
|||||||
ToolChoice any `json:"tool_choice,omitempty"`
|
ToolChoice any `json:"tool_choice,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r ChatCompletionRequest) GetFunctionCate() string {
|
||||||
|
if r.Tools != nil {
|
||||||
|
return "tool"
|
||||||
|
} else if r.Functions != nil {
|
||||||
|
return "function"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
type ChatCompletionFunction struct {
|
type ChatCompletionFunction struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
@ -157,10 +167,10 @@ type ChatCompletionResponse struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ChatCompletionStreamChoiceDelta struct {
|
type ChatCompletionStreamChoiceDelta struct {
|
||||||
Content string `json:"content,omitempty"`
|
Content string `json:"content,omitempty"`
|
||||||
Role string `json:"role,omitempty"`
|
Role string `json:"role,omitempty"`
|
||||||
FunctionCall any `json:"function_call,omitempty"`
|
FunctionCall *ChatCompletionToolCallsFunction `json:"function_call,omitempty"`
|
||||||
ToolCalls any `json:"tool_calls,omitempty"`
|
ToolCalls []*ChatCompletionToolCalls `json:"tool_calls,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChatCompletionStreamChoice struct {
|
type ChatCompletionStreamChoice struct {
|
||||||
|
Loading…
Reference in New Issue
Block a user