🎨 Support qwen-vl-plus

This commit is contained in:
Martial BE 2023-12-29 10:59:26 +08:00
parent c4c89e8e1b
commit 211a862d54
No known key found for this signature in database
GPG Key ID: D06C32DF0EDB9084
5 changed files with 107 additions and 44 deletions

View File

@ -93,6 +93,7 @@ var ModelRatio = map[string]float64{
"qwen-plus": 1.4286, // ¥0.02 / 1k tokens "qwen-plus": 1.4286, // ¥0.02 / 1k tokens
"qwen-max": 1.4286, // ¥0.02 / 1k tokens "qwen-max": 1.4286, // ¥0.02 / 1k tokens
"qwen-max-longcontext": 1.4286, // ¥0.02 / 1k tokens "qwen-max-longcontext": 1.4286, // ¥0.02 / 1k tokens
"qwen-vl-plus": 0.5715, // ¥0.008 / 1k tokens
"text-embedding-v1": 0.05, // ¥0.0007 / 1k tokens "text-embedding-v1": 0.05, // ¥0.0007 / 1k tokens
"SparkDesk": 1.2858, // ¥0.018 / 1k tokens "SparkDesk": 1.2858, // ¥0.018 / 1k tokens
"360GPT_S2_V9": 0.8572, // ¥0.012 / 1k tokens "360GPT_S2_V9": 0.8572, // ¥0.012 / 1k tokens

View File

@ -2,6 +2,7 @@ package ali
import ( import (
"fmt" "fmt"
"strings"
"one-api/providers/base" "one-api/providers/base"
@ -28,6 +29,16 @@ type AliProvider struct {
base.BaseProvider base.BaseProvider
} }
func (p *AliProvider) GetFullRequestURL(requestURL string, modelName string) string {
baseURL := strings.TrimSuffix(p.GetBaseURL(), "/")
if modelName == "qwen-vl-plus" {
requestURL = "/api/v1/services/aigc/multimodal-generation/generation"
}
return fmt.Sprintf("%s%s", baseURL, requestURL)
}
// 获取请求头 // 获取请求头
func (p *AliProvider) GetRequestHeaders() (headers map[string]string) { func (p *AliProvider) GetRequestHeaders() (headers map[string]string) {
headers = make(map[string]string) headers = make(map[string]string)

View File

@ -26,21 +26,12 @@ func (aliResponse *AliChatResponse) ResponseHandler(resp *http.Response) (OpenAI
return return
} }
choice := types.ChatCompletionChoice{
Index: 0,
Message: types.ChatCompletionMessage{
Role: "assistant",
Content: aliResponse.Output.Text,
},
FinishReason: aliResponse.Output.FinishReason,
}
OpenAIResponse = types.ChatCompletionResponse{ OpenAIResponse = types.ChatCompletionResponse{
ID: aliResponse.RequestId, ID: aliResponse.RequestId,
Object: "chat.completion", Object: "chat.completion",
Created: common.GetTimestamp(), Created: common.GetTimestamp(),
Model: aliResponse.Model, Model: aliResponse.Model,
Choices: []types.ChatCompletionChoice{choice}, Choices: aliResponse.Output.ToChatCompletionChoices(),
Usage: &types.Usage{ Usage: &types.Usage{
PromptTokens: aliResponse.Usage.InputTokens, PromptTokens: aliResponse.Usage.InputTokens,
CompletionTokens: aliResponse.Usage.OutputTokens, CompletionTokens: aliResponse.Usage.OutputTokens,
@ -58,10 +49,31 @@ func (p *AliProvider) getChatRequestBody(request *types.ChatCompletionRequest) *
messages := make([]AliMessage, 0, len(request.Messages)) messages := make([]AliMessage, 0, len(request.Messages))
for i := 0; i < len(request.Messages); i++ { for i := 0; i < len(request.Messages); i++ {
message := request.Messages[i] message := request.Messages[i]
messages = append(messages, AliMessage{ if request.Model != "qwen-vl-plus" {
Content: message.StringContent(), messages = append(messages, AliMessage{
Role: strings.ToLower(message.Role), Content: message.StringContent(),
}) Role: strings.ToLower(message.Role),
})
} else {
openaiContent := message.ParseContent()
var parts []AliMessagePart
for _, part := range openaiContent {
if part.Type == types.ContentTypeText {
parts = append(parts, AliMessagePart{
Text: part.Text,
})
} else if part.Type == types.ContentTypeImageURL {
parts = append(parts, AliMessagePart{
Image: part.ImageURL.URL,
})
}
}
messages = append(messages, AliMessage{
Content: parts,
Role: strings.ToLower(message.Role),
})
}
} }
enableSearch := false enableSearch := false
@ -77,6 +89,7 @@ func (p *AliProvider) getChatRequestBody(request *types.ChatCompletionRequest) *
Messages: messages, Messages: messages,
}, },
Parameters: AliParameters{ Parameters: AliParameters{
ResultFormat: "message",
EnableSearch: enableSearch, EnableSearch: enableSearch,
IncrementalOutput: request.Stream, IncrementalOutput: request.Stream,
}, },
@ -87,6 +100,7 @@ func (p *AliProvider) getChatRequestBody(request *types.ChatCompletionRequest) *
func (p *AliProvider) ChatAction(request *types.ChatCompletionRequest, isModelMapped bool, promptTokens int) (usage *types.Usage, errWithCode *types.OpenAIErrorWithStatusCode) { func (p *AliProvider) ChatAction(request *types.ChatCompletionRequest, isModelMapped bool, promptTokens int) (usage *types.Usage, errWithCode *types.OpenAIErrorWithStatusCode) {
requestBody := p.getChatRequestBody(request) requestBody := p.getChatRequestBody(request)
fullRequestURL := p.GetFullRequestURL(p.ChatCompletions, request.Model) fullRequestURL := p.GetFullRequestURL(p.ChatCompletions, request.Model)
headers := p.GetRequestHeaders() headers := p.GetRequestHeaders()
if request.Stream { if request.Stream {
@ -134,10 +148,15 @@ func (p *AliProvider) ChatAction(request *types.ChatCompletionRequest, isModelMa
// 阿里云响应转OpenAI响应 // 阿里云响应转OpenAI响应
func (p *AliProvider) streamResponseAli2OpenAI(aliResponse *AliChatResponse) *types.ChatCompletionStreamResponse { func (p *AliProvider) streamResponseAli2OpenAI(aliResponse *AliChatResponse) *types.ChatCompletionStreamResponse {
// chatChoice := aliResponse.Output.ToChatCompletionChoices()
// jsonBody, _ := json.MarshalIndent(chatChoice, "", " ")
// fmt.Println("requestBody:", string(jsonBody))
var choice types.ChatCompletionStreamChoice var choice types.ChatCompletionStreamChoice
choice.Delta.Content = aliResponse.Output.Text choice.Index = aliResponse.Output.Choices[0].Index
if aliResponse.Output.FinishReason != "null" { choice.Delta.Content = aliResponse.Output.Choices[0].Message.StringContent()
finishReason := aliResponse.Output.FinishReason // fmt.Println("choice.Delta.Content:", chatChoice[0].Message)
if aliResponse.Output.Choices[0].FinishReason != "null" {
finishReason := aliResponse.Output.Choices[0].FinishReason
choice.FinishReason = &finishReason choice.FinishReason = &finishReason
} }
@ -200,7 +219,8 @@ func (p *AliProvider) sendStreamRequest(req *http.Request, model string) (usage
stopChan <- true stopChan <- true
}() }()
common.SetEventStreamHeaders(p.Context) common.SetEventStreamHeaders(p.Context)
// lastResponseText := "" lastResponseText := ""
index := 0
p.Context.Stream(func(w io.Writer) bool { p.Context.Stream(func(w io.Writer) bool {
select { select {
case data := <-dataChan: case data := <-dataChan:
@ -216,9 +236,11 @@ func (p *AliProvider) sendStreamRequest(req *http.Request, model string) (usage
usage.TotalTokens = aliResponse.Usage.InputTokens + aliResponse.Usage.OutputTokens usage.TotalTokens = aliResponse.Usage.InputTokens + aliResponse.Usage.OutputTokens
} }
aliResponse.Model = model aliResponse.Model = model
aliResponse.Output.Choices[0].Index = index
index++
response := p.streamResponseAli2OpenAI(&aliResponse) response := p.streamResponseAli2OpenAI(&aliResponse)
// response.Choices[0].Delta.Content = strings.TrimPrefix(response.Choices[0].Delta.Content, lastResponseText) response.Choices[0].Delta.Content = strings.TrimPrefix(response.Choices[0].Delta.Content, lastResponseText)
// lastResponseText = aliResponse.Output.Text lastResponseText = aliResponse.Output.Choices[0].Message.StringContent()
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())

View File

@ -1,5 +1,9 @@
package ali package ali
import (
"one-api/types"
)
type AliError struct { type AliError struct {
Code string `json:"code"` Code string `json:"code"`
Message string `json:"message"` Message string `json:"message"`
@ -13,10 +17,15 @@ type AliUsage struct {
} }
type AliMessage struct { type AliMessage struct {
Content string `json:"content"` Content any `json:"content"`
Role string `json:"role"` Role string `json:"role"`
} }
type AliMessagePart struct {
Text string `json:"text,omitempty"`
Image string `json:"image,omitempty"`
}
type AliInput struct { type AliInput struct {
// Prompt string `json:"prompt"` // Prompt string `json:"prompt"`
Messages []AliMessage `json:"messages"` Messages []AliMessage `json:"messages"`
@ -28,6 +37,7 @@ type AliParameters struct {
Seed uint64 `json:"seed,omitempty"` Seed uint64 `json:"seed,omitempty"`
EnableSearch bool `json:"enable_search,omitempty"` EnableSearch bool `json:"enable_search,omitempty"`
IncrementalOutput bool `json:"incremental_output,omitempty"` IncrementalOutput bool `json:"incremental_output,omitempty"`
ResultFormat string `json:"result_format,omitempty"`
} }
type AliChatRequest struct { type AliChatRequest struct {
@ -36,9 +46,25 @@ type AliChatRequest struct {
Parameters AliParameters `json:"parameters,omitempty"` Parameters AliParameters `json:"parameters,omitempty"`
} }
type AliChoice struct {
FinishReason string `json:"finish_reason"`
Message types.ChatCompletionMessage `json:"message"`
}
type AliOutput struct { type AliOutput struct {
Text string `json:"text"` Choices []types.ChatCompletionChoice `json:"choices"`
FinishReason string `json:"finish_reason"` }
func (o *AliOutput) ToChatCompletionChoices() []types.ChatCompletionChoice {
for i := range o.Choices {
_, ok := o.Choices[i].Message.Content.(string)
if ok {
continue
}
o.Choices[i].Message.Content = o.Choices[i].Message.ParseContent()
}
return o.Choices
} }
type AliChatResponse struct { type AliChatResponse struct {

View File

@ -27,11 +27,11 @@ func (m ChatCompletionMessage) StringContent() string {
if !ok { if !ok {
continue continue
} }
if contentMap["type"] == "text" {
if subStr, ok := contentMap["text"].(string); ok { if subStr, ok := contentMap["text"].(string); ok && subStr != "" {
contentStr += subStr contentStr += subStr
}
} }
} }
return contentStr return contentStr
} }
@ -55,23 +55,26 @@ func (m ChatCompletionMessage) ParseContent() []ChatMessagePart {
if !ok { if !ok {
continue continue
} }
switch contentMap["type"] {
case ContentTypeText: if subStr, ok := contentMap["text"].(string); ok && subStr != "" {
if subStr, ok := contentMap["text"].(string); ok { contentList = append(contentList, ChatMessagePart{
contentList = append(contentList, ChatMessagePart{ Type: ContentTypeText,
Type: ContentTypeText, Text: subStr,
Text: subStr, })
}) } else if subObj, ok := contentMap["image_url"].(map[string]any); ok {
} contentList = append(contentList, ChatMessagePart{
case ContentTypeImageURL: Type: ContentTypeImageURL,
if subObj, ok := contentMap["image_url"].(map[string]any); ok { ImageURL: &ChatMessageImageURL{
contentList = append(contentList, ChatMessagePart{ URL: subObj["url"].(string),
Type: ContentTypeImageURL, },
ImageURL: &ChatMessageImageURL{ })
URL: subObj["url"].(string), } else if subObj, ok := contentMap["image"].(string); ok {
}, contentList = append(contentList, ChatMessagePart{
}) Type: ContentTypeImageURL,
} ImageURL: &ChatMessageImageURL{
URL: subObj,
},
})
} }
} }
return contentList return contentList