✨ feat: MiniMax support (#50)
This commit is contained in:
parent
2cc120f35b
commit
705804e6dd
@ -190,6 +190,7 @@ const (
|
|||||||
ChannelTypeAzureSpeech = 24
|
ChannelTypeAzureSpeech = 24
|
||||||
ChannelTypeGemini = 25
|
ChannelTypeGemini = 25
|
||||||
ChannelTypeBaichuan = 26
|
ChannelTypeBaichuan = 26
|
||||||
|
ChannelTypeMiniMax = 27
|
||||||
)
|
)
|
||||||
|
|
||||||
var ChannelBaseURLs = []string{
|
var ChannelBaseURLs = []string{
|
||||||
@ -220,6 +221,7 @@ var ChannelBaseURLs = []string{
|
|||||||
"", //24
|
"", //24
|
||||||
"", //25
|
"", //25
|
||||||
"https://api.baichuan-ai.com", //26
|
"https://api.baichuan-ai.com", //26
|
||||||
|
"https://api.minimax.chat/v1", //27
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -99,6 +99,10 @@ func init() {
|
|||||||
"Baichuan2-Turbo-192k": {1.143, ChannelTypeBaichuan}, // ¥0.016 / 1k tokens
|
"Baichuan2-Turbo-192k": {1.143, ChannelTypeBaichuan}, // ¥0.016 / 1k tokens
|
||||||
"Baichuan2-53B": {1.4286, ChannelTypeBaichuan}, // ¥0.02 / 1k tokens
|
"Baichuan2-53B": {1.4286, ChannelTypeBaichuan}, // ¥0.02 / 1k tokens
|
||||||
"Baichuan-Text-Embedding": {0.0357, ChannelTypeBaichuan}, // ¥0.0005 / 1k tokens
|
"Baichuan-Text-Embedding": {0.0357, ChannelTypeBaichuan}, // ¥0.0005 / 1k tokens
|
||||||
|
"abab5.5s-chat": {0.3572, ChannelTypeMiniMax}, // ¥0.005 / 1k tokens
|
||||||
|
"abab5.5-chat": {1.0714, ChannelTypeMiniMax}, // ¥0.015 / 1k tokens
|
||||||
|
"abab6-chat": {14.2857, ChannelTypeMiniMax}, // ¥0.2 / 1k tokens
|
||||||
|
"embo-01": {0.0357, ChannelTypeMiniMax}, // ¥0.0005 / 1k tokens
|
||||||
}
|
}
|
||||||
|
|
||||||
ModelRatio = make(map[string]float64)
|
ModelRatio = make(map[string]float64)
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"one-api/common"
|
"one-api/common"
|
||||||
"one-api/types"
|
"one-api/types"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
@ -122,7 +123,7 @@ func (r *HTTPRequester) SendRequestRaw(req *http.Request) (*http.Response, *type
|
|||||||
// 获取流式响应
|
// 获取流式响应
|
||||||
func RequestStream[T streamable](requester *HTTPRequester, resp *http.Response, handlerPrefix HandlerPrefix[T]) (*streamReader[T], *types.OpenAIErrorWithStatusCode) {
|
func RequestStream[T streamable](requester *HTTPRequester, resp *http.Response, handlerPrefix HandlerPrefix[T]) (*streamReader[T], *types.OpenAIErrorWithStatusCode) {
|
||||||
// 如果返回的头是json格式 说明有错误
|
// 如果返回的头是json格式 说明有错误
|
||||||
if resp.Header.Get("Content-Type") == "application/json" {
|
if strings.Contains(resp.Header.Get("Content-Type"), "application/json") {
|
||||||
return nil, HandleErrorResp(resp, requester.ErrorHandler)
|
return nil, HandleErrorResp(resp, requester.ErrorHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
type Channel struct {
|
type Channel struct {
|
||||||
Id int `json:"id"`
|
Id int `json:"id"`
|
||||||
Type int `json:"type" gorm:"default:0"`
|
Type int `json:"type" gorm:"default:0"`
|
||||||
Key string `json:"key" gorm:"not null;index"`
|
Key string `json:"key" gorm:"type:varchar(767);not null;index"`
|
||||||
Status int `json:"status" gorm:"default:1"`
|
Status int `json:"status" gorm:"default:1"`
|
||||||
Name string `json:"name" gorm:"index"`
|
Name string `json:"name" gorm:"index"`
|
||||||
Weight *uint `json:"weight" gorm:"default:0"`
|
Weight *uint `json:"weight" gorm:"default:0"`
|
||||||
|
115
providers/minimax/base.go
Normal file
115
providers/minimax/base.go
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
package minimax
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"one-api/common/requester"
|
||||||
|
"one-api/model"
|
||||||
|
"one-api/providers/base"
|
||||||
|
"one-api/types"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MiniMaxProviderFactory struct{}
|
||||||
|
|
||||||
|
// 创建 MiniMaxProvider
|
||||||
|
func (f MiniMaxProviderFactory) Create(channel *model.Channel) base.ProviderInterface {
|
||||||
|
return &MiniMaxProvider{
|
||||||
|
BaseProvider: base.BaseProvider{
|
||||||
|
Config: getConfig(),
|
||||||
|
Channel: channel,
|
||||||
|
Requester: requester.NewHTTPRequester(*channel.Proxy, requestErrorHandle),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type MiniMaxProvider struct {
|
||||||
|
base.BaseProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
func getConfig() base.ProviderConfig {
|
||||||
|
return base.ProviderConfig{
|
||||||
|
BaseURL: "https://api.minimax.chat/v1",
|
||||||
|
ChatCompletions: "/text/chatcompletion_pro",
|
||||||
|
Embeddings: "/embeddings",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 请求错误处理
|
||||||
|
func requestErrorHandle(resp *http.Response) *types.OpenAIError {
|
||||||
|
minimaxError := &MiniMaxBaseResp{}
|
||||||
|
err := json.NewDecoder(resp.Body).Decode(minimaxError)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return errorHandle(&minimaxError.BaseResp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 错误处理
|
||||||
|
func errorHandle(minimaxError *BaseResp) *types.OpenAIError {
|
||||||
|
if minimaxError.StatusCode == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &types.OpenAIError{
|
||||||
|
Message: minimaxError.StatusMsg,
|
||||||
|
Type: "minimax_error",
|
||||||
|
Code: minimaxError.StatusCode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *MiniMaxProvider) GetFullRequestURL(requestURL string, modelName string) string {
|
||||||
|
baseURL := strings.TrimSuffix(p.GetBaseURL(), "/")
|
||||||
|
keys := strings.Split(p.Channel.Key, "|")
|
||||||
|
if len(keys) != 2 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s%s?GroupId=%s", baseURL, requestURL, keys[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取请求头
|
||||||
|
func (p *MiniMaxProvider) GetRequestHeaders() (headers map[string]string) {
|
||||||
|
headers = make(map[string]string)
|
||||||
|
p.CommonRequestHeaders(headers)
|
||||||
|
keys := strings.Split(p.Channel.Key, "|")
|
||||||
|
|
||||||
|
headers["Authorization"] = "Bearer " + keys[0]
|
||||||
|
|
||||||
|
return headers
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultBot() MiniMaxBotSetting {
|
||||||
|
return MiniMaxBotSetting{
|
||||||
|
BotName: types.ChatMessageRoleAssistant,
|
||||||
|
Content: "You are a helpful assistant. You can help me by answering my questions. You can also ask me questions.",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultReplyConstraints() ReplyConstraints {
|
||||||
|
return ReplyConstraints{
|
||||||
|
SenderType: "BOT",
|
||||||
|
SenderName: types.ChatMessageRoleAssistant,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertRole(roleName string) (string, string) {
|
||||||
|
switch roleName {
|
||||||
|
case types.ChatMessageRoleTool, types.ChatMessageRoleFunction:
|
||||||
|
return "FUNCTION", types.ChatMessageRoleAssistant
|
||||||
|
case types.ChatMessageRoleSystem, types.ChatMessageRoleAssistant:
|
||||||
|
return "BOT", types.ChatMessageRoleAssistant
|
||||||
|
default:
|
||||||
|
return "USER", types.ChatMessageRoleUser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertFinishReason(finishReason string) string {
|
||||||
|
switch finishReason {
|
||||||
|
case "max_output":
|
||||||
|
return types.FinishReasonLength
|
||||||
|
default:
|
||||||
|
return finishReason
|
||||||
|
}
|
||||||
|
}
|
288
providers/minimax/chat.go
Normal file
288
providers/minimax/chat.go
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
package minimax
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"one-api/common"
|
||||||
|
"one-api/common/requester"
|
||||||
|
"one-api/types"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type minimaxStreamHandler struct {
|
||||||
|
Usage *types.Usage
|
||||||
|
Request *types.ChatCompletionRequest
|
||||||
|
LastContent string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *MiniMaxProvider) CreateChatCompletion(request *types.ChatCompletionRequest) (*types.ChatCompletionResponse, *types.OpenAIErrorWithStatusCode) {
|
||||||
|
req, errWithCode := p.getChatRequest(request)
|
||||||
|
if errWithCode != nil {
|
||||||
|
return nil, errWithCode
|
||||||
|
}
|
||||||
|
defer req.Body.Close()
|
||||||
|
|
||||||
|
response := &MiniMaxChatResponse{}
|
||||||
|
// 发送请求
|
||||||
|
_, errWithCode = p.Requester.SendRequest(req, response, false)
|
||||||
|
if errWithCode != nil {
|
||||||
|
return nil, errWithCode
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.convertToChatOpenai(response, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *MiniMaxProvider) CreateChatCompletionStream(request *types.ChatCompletionRequest) (requester.StreamReaderInterface[types.ChatCompletionStreamResponse], *types.OpenAIErrorWithStatusCode) {
|
||||||
|
req, errWithCode := p.getChatRequest(request)
|
||||||
|
if errWithCode != nil {
|
||||||
|
return nil, errWithCode
|
||||||
|
}
|
||||||
|
defer req.Body.Close()
|
||||||
|
|
||||||
|
// 发送请求
|
||||||
|
resp, errWithCode := p.Requester.SendRequestRaw(req)
|
||||||
|
if errWithCode != nil {
|
||||||
|
return nil, errWithCode
|
||||||
|
}
|
||||||
|
|
||||||
|
chatHandler := &minimaxStreamHandler{
|
||||||
|
Usage: p.Usage,
|
||||||
|
Request: request,
|
||||||
|
}
|
||||||
|
|
||||||
|
return requester.RequestStream[types.ChatCompletionStreamResponse](p.Requester, resp, chatHandler.handlerStream)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *MiniMaxProvider) getChatRequest(request *types.ChatCompletionRequest) (*http.Request, *types.OpenAIErrorWithStatusCode) {
|
||||||
|
url, errWithCode := p.GetSupportedAPIUri(common.RelayModeChatCompletions)
|
||||||
|
if errWithCode != nil {
|
||||||
|
return nil, errWithCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取请求地址
|
||||||
|
fullRequestURL := p.GetFullRequestURL(url, request.Model)
|
||||||
|
if fullRequestURL == "" {
|
||||||
|
return nil, common.ErrorWrapper(nil, "invalid_minimax_config", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取请求头
|
||||||
|
headers := p.GetRequestHeaders()
|
||||||
|
|
||||||
|
zhipuRequest := convertFromChatOpenai(request)
|
||||||
|
|
||||||
|
// 创建请求
|
||||||
|
req, err := p.Requester.NewRequest(http.MethodPost, fullRequestURL, p.Requester.WithBody(zhipuRequest), p.Requester.WithHeader(headers))
|
||||||
|
if err != nil {
|
||||||
|
return nil, common.ErrorWrapper(err, "new_request_failed", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *MiniMaxProvider) convertToChatOpenai(response *MiniMaxChatResponse, request *types.ChatCompletionRequest) (openaiResponse *types.ChatCompletionResponse, errWithCode *types.OpenAIErrorWithStatusCode) {
|
||||||
|
error := errorHandle(&response.MiniMaxBaseResp.BaseResp)
|
||||||
|
if error != nil {
|
||||||
|
errWithCode = &types.OpenAIErrorWithStatusCode{
|
||||||
|
OpenAIError: *error,
|
||||||
|
StatusCode: http.StatusBadRequest,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
openaiResponse = &types.ChatCompletionResponse{
|
||||||
|
ID: response.ID,
|
||||||
|
Object: "chat.completion",
|
||||||
|
Created: response.Created,
|
||||||
|
Model: response.Model,
|
||||||
|
Choices: make([]types.ChatCompletionChoice, 0, len(response.Choices)),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, choice := range response.Choices {
|
||||||
|
openaiChoice := types.ChatCompletionChoice{
|
||||||
|
FinishReason: convertFinishReason(choice.FinishReason),
|
||||||
|
}
|
||||||
|
if choice.Messages[0].FunctionCall != nil {
|
||||||
|
if request.Functions != nil {
|
||||||
|
openaiChoice.Message.FunctionCall = choice.Messages[0].FunctionCall
|
||||||
|
} else {
|
||||||
|
openaiChoice.Message.ToolCalls = append(openaiChoice.Message.ToolCalls, &types.ChatCompletionToolCalls{
|
||||||
|
Type: types.ChatMessageRoleFunction,
|
||||||
|
Function: choice.Messages[0].FunctionCall,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
openaiChoice.Message.Role = choice.Messages[0].SenderName
|
||||||
|
openaiChoice.Message.Content = choice.Messages[0].Text
|
||||||
|
}
|
||||||
|
openaiResponse.Choices = append(openaiResponse.Choices, openaiChoice)
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.Usage.TotalTokens < p.Usage.PromptTokens {
|
||||||
|
p.Usage.PromptTokens = response.Usage.TotalTokens
|
||||||
|
}
|
||||||
|
p.Usage.TotalTokens = response.Usage.TotalTokens
|
||||||
|
p.Usage.CompletionTokens = response.Usage.TotalTokens - p.Usage.PromptTokens
|
||||||
|
|
||||||
|
openaiResponse.Usage = p.Usage
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertFromChatOpenai(request *types.ChatCompletionRequest) *MiniMaxChatRequest {
|
||||||
|
var botSettings []MiniMaxBotSetting
|
||||||
|
var messges []MiniMaxChatMessage
|
||||||
|
for _, message := range request.Messages {
|
||||||
|
if message.Role == types.ChatMessageRoleSystem {
|
||||||
|
botSettings = append(botSettings, MiniMaxBotSetting{
|
||||||
|
BotName: types.ChatMessageRoleAssistant,
|
||||||
|
Content: message.StringContent(),
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
miniMessage := MiniMaxChatMessage{
|
||||||
|
Text: message.StringContent(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果role为function, 则需要在前面一条记录添加function_call,如果没有消息,则添加一个message
|
||||||
|
if message.Role == types.ChatMessageRoleFunction {
|
||||||
|
if len(messges) == 0 {
|
||||||
|
messges = append(messges, MiniMaxChatMessage{
|
||||||
|
SenderType: "USER",
|
||||||
|
SenderName: types.ChatMessageRoleUser,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
messges[len(messges)-1].FunctionCall = &types.ChatCompletionToolCallsFunction{
|
||||||
|
Name: "funciton",
|
||||||
|
Arguments: "arguments",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
miniMessage.SenderType, miniMessage.SenderName = convertRole(message.Role)
|
||||||
|
|
||||||
|
messges = append(messges, miniMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(botSettings) == 0 {
|
||||||
|
botSettings = append(botSettings, defaultBot())
|
||||||
|
}
|
||||||
|
|
||||||
|
miniRequest := &MiniMaxChatRequest{
|
||||||
|
Model: request.Model,
|
||||||
|
Messages: messges,
|
||||||
|
Stream: request.Stream,
|
||||||
|
Temperature: request.Temperature,
|
||||||
|
TopP: request.TopP,
|
||||||
|
TokensToGenerate: request.MaxTokens,
|
||||||
|
BotSetting: botSettings,
|
||||||
|
ReplyConstraints: defaultReplyConstraints(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if request.Functions != nil {
|
||||||
|
miniRequest.Functions = request.Functions
|
||||||
|
} else if request.Tools != nil {
|
||||||
|
miniRequest.Functions = make([]*types.ChatCompletionFunction, 0, len(request.Tools))
|
||||||
|
for _, tool := range request.Tools {
|
||||||
|
miniRequest.Functions = append(miniRequest.Functions, &tool.Function)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return miniRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换为OpenAI聊天流式请求体
|
||||||
|
func (h *minimaxStreamHandler) handlerStream(rawLine *[]byte, isFinished *bool, response *[]types.ChatCompletionStreamResponse) error {
|
||||||
|
// 如果rawLine 前缀不为data: 或者 meta:,则直接返回
|
||||||
|
if !strings.HasPrefix(string(*rawLine), "data: ") {
|
||||||
|
*rawLine = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
*rawLine = (*rawLine)[6:]
|
||||||
|
|
||||||
|
miniResponse := &MiniMaxChatResponse{}
|
||||||
|
err := json.Unmarshal(*rawLine, miniResponse)
|
||||||
|
if err != nil {
|
||||||
|
return common.ErrorToOpenAIError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
error := errorHandle(&miniResponse.BaseResp)
|
||||||
|
if error != nil {
|
||||||
|
return error
|
||||||
|
}
|
||||||
|
|
||||||
|
choice := miniResponse.Choices[0]
|
||||||
|
|
||||||
|
if choice.Messages[0].FunctionCall != nil && choice.FinishReason == "" {
|
||||||
|
*rawLine = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return h.convertToOpenaiStream(miniResponse, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *minimaxStreamHandler) convertToOpenaiStream(miniResponse *MiniMaxChatResponse, response *[]types.ChatCompletionStreamResponse) error {
|
||||||
|
streamResponse := types.ChatCompletionStreamResponse{
|
||||||
|
ID: miniResponse.RequestID,
|
||||||
|
Object: "chat.completion.chunk",
|
||||||
|
Created: miniResponse.Created,
|
||||||
|
Model: h.Request.Model,
|
||||||
|
}
|
||||||
|
|
||||||
|
miniChoice := miniResponse.Choices[0]
|
||||||
|
openaiChoice := types.ChatCompletionStreamChoice{}
|
||||||
|
|
||||||
|
if miniChoice.Messages[0].FunctionCall == nil && miniChoice.FinishReason != "" {
|
||||||
|
streamResponse.ID = miniResponse.ID
|
||||||
|
openaiChoice.FinishReason = convertFinishReason(miniChoice.FinishReason)
|
||||||
|
h.appendResponse(&streamResponse, &openaiChoice, response)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
openaiChoice.Delta = types.ChatCompletionStreamChoiceDelta{
|
||||||
|
Role: miniChoice.Messages[0].SenderName,
|
||||||
|
}
|
||||||
|
|
||||||
|
if miniChoice.Messages[0].FunctionCall != nil {
|
||||||
|
h.handleFunctionCall(&miniChoice, &openaiChoice)
|
||||||
|
convertChoices := openaiChoice.ConvertOpenaiStream()
|
||||||
|
for _, convertChoice := range convertChoices {
|
||||||
|
chatCompletionCopy := streamResponse
|
||||||
|
h.appendResponse(&chatCompletionCopy, &convertChoice, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
openaiChoice.Delta.Content = miniChoice.Messages[0].Text
|
||||||
|
h.appendResponse(&streamResponse, &openaiChoice, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
if miniResponse.Usage != nil {
|
||||||
|
h.handleUsage(miniResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *minimaxStreamHandler) handleFunctionCall(choice *Choice, openaiChoice *types.ChatCompletionStreamChoice) {
|
||||||
|
if h.Request.Functions != nil {
|
||||||
|
openaiChoice.Delta.FunctionCall = choice.Messages[0].FunctionCall
|
||||||
|
} else {
|
||||||
|
openaiChoice.Delta.ToolCalls = append(openaiChoice.Delta.ToolCalls, &types.ChatCompletionToolCalls{
|
||||||
|
Type: types.ChatMessageRoleFunction,
|
||||||
|
Function: choice.Messages[0].FunctionCall,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *minimaxStreamHandler) appendResponse(streamResponse *types.ChatCompletionStreamResponse, openaiChoice *types.ChatCompletionStreamChoice, response *[]types.ChatCompletionStreamResponse) {
|
||||||
|
streamResponse.Choices = []types.ChatCompletionStreamChoice{*openaiChoice}
|
||||||
|
*response = append(*response, *streamResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *minimaxStreamHandler) handleUsage(miniResponse *MiniMaxChatResponse) {
|
||||||
|
if miniResponse.Usage.TotalTokens < h.Usage.PromptTokens {
|
||||||
|
h.Usage.PromptTokens = miniResponse.Usage.TotalTokens
|
||||||
|
}
|
||||||
|
h.Usage.TotalTokens = miniResponse.Usage.TotalTokens
|
||||||
|
h.Usage.CompletionTokens = miniResponse.Usage.TotalTokens - h.Usage.PromptTokens
|
||||||
|
}
|
93
providers/minimax/embeddings.go
Normal file
93
providers/minimax/embeddings.go
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
package minimax
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"one-api/common"
|
||||||
|
"one-api/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *MiniMaxProvider) CreateEmbeddings(request *types.EmbeddingRequest) (*types.EmbeddingResponse, *types.OpenAIErrorWithStatusCode) {
|
||||||
|
url, errWithCode := p.GetSupportedAPIUri(common.RelayModeEmbeddings)
|
||||||
|
if errWithCode != nil {
|
||||||
|
return nil, errWithCode
|
||||||
|
}
|
||||||
|
// 获取请求地址
|
||||||
|
fullRequestURL := p.GetFullRequestURL(url, request.Model)
|
||||||
|
if fullRequestURL == "" {
|
||||||
|
return nil, common.ErrorWrapper(nil, "invalid_minimax_config", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取请求头
|
||||||
|
headers := p.GetRequestHeaders()
|
||||||
|
|
||||||
|
minimaxRequest := convertFromEmbeddingOpenai(request)
|
||||||
|
|
||||||
|
// 创建请求
|
||||||
|
req, err := p.Requester.NewRequest(http.MethodPost, fullRequestURL, p.Requester.WithBody(minimaxRequest), p.Requester.WithHeader(headers))
|
||||||
|
if err != nil {
|
||||||
|
return nil, common.ErrorWrapper(err, "new_request_failed", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
defer req.Body.Close()
|
||||||
|
|
||||||
|
minimaxResponse := &MiniMaxEmbeddingResponse{}
|
||||||
|
|
||||||
|
// 发送请求
|
||||||
|
_, errWithCode = p.Requester.SendRequest(req, minimaxResponse, false)
|
||||||
|
if errWithCode != nil {
|
||||||
|
return nil, errWithCode
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.convertToEmbeddingOpenai(minimaxResponse, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertFromEmbeddingOpenai(request *types.EmbeddingRequest) *MiniMaxEmbeddingRequest {
|
||||||
|
minimaxRequest := &MiniMaxEmbeddingRequest{
|
||||||
|
Model: request.Model,
|
||||||
|
Type: "db",
|
||||||
|
}
|
||||||
|
|
||||||
|
if input, ok := request.Input.(string); ok {
|
||||||
|
minimaxRequest.Texts = []string{input}
|
||||||
|
} else if inputs, ok := request.Input.([]any); ok {
|
||||||
|
for _, item := range inputs {
|
||||||
|
if input, ok := item.(string); ok {
|
||||||
|
minimaxRequest.Texts = append(minimaxRequest.Texts, input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return minimaxRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *MiniMaxProvider) convertToEmbeddingOpenai(response *MiniMaxEmbeddingResponse, request *types.EmbeddingRequest) (openaiResponse *types.EmbeddingResponse, errWithCode *types.OpenAIErrorWithStatusCode) {
|
||||||
|
error := errorHandle(&response.BaseResp)
|
||||||
|
if error != nil {
|
||||||
|
errWithCode = &types.OpenAIErrorWithStatusCode{
|
||||||
|
OpenAIError: *error,
|
||||||
|
StatusCode: http.StatusBadRequest,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
openaiResponse = &types.EmbeddingResponse{
|
||||||
|
Object: "list",
|
||||||
|
Model: request.Model,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range response.Vectors {
|
||||||
|
openaiResponse.Data = append(openaiResponse.Data, types.Embedding{
|
||||||
|
Object: "embedding",
|
||||||
|
Embedding: item,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.TotalTokens < p.Usage.PromptTokens {
|
||||||
|
p.Usage.PromptTokens = response.TotalTokens
|
||||||
|
}
|
||||||
|
p.Usage.TotalTokens = response.TotalTokens
|
||||||
|
p.Usage.CompletionTokens = response.TotalTokens - p.Usage.PromptTokens
|
||||||
|
|
||||||
|
openaiResponse.Usage = p.Usage
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
79
providers/minimax/type.go
Normal file
79
providers/minimax/type.go
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
package minimax
|
||||||
|
|
||||||
|
import "one-api/types"
|
||||||
|
|
||||||
|
type MiniMaxChatRequest struct {
|
||||||
|
Model string `json:"model"`
|
||||||
|
Stream bool `json:"stream,omitempty"`
|
||||||
|
TokensToGenerate int `json:"tokens_to_generate,omitempty"`
|
||||||
|
Temperature float64 `json:"temperature,omitempty"`
|
||||||
|
TopP float64 `json:"top_p,omitempty"`
|
||||||
|
Messages []MiniMaxChatMessage `json:"messages"`
|
||||||
|
BotSetting []MiniMaxBotSetting `json:"bot_setting,omitempty"`
|
||||||
|
ReplyConstraints ReplyConstraints `json:"reply_constraints,omitempty"`
|
||||||
|
Functions []*types.ChatCompletionFunction `json:"functions,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MiniMaxChatMessage struct {
|
||||||
|
SenderType string `json:"sender_type"`
|
||||||
|
SenderName string `json:"sender_name"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
FunctionCall *types.ChatCompletionToolCallsFunction `json:"function_call,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MiniMaxBotSetting struct {
|
||||||
|
BotName string `json:"bot_name"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReplyConstraints struct {
|
||||||
|
SenderType string `json:"sender_type"`
|
||||||
|
SenderName string `json:"sender_name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MiniMaxChatResponse struct {
|
||||||
|
Created int64 `json:"created"`
|
||||||
|
Model string `json:"model"`
|
||||||
|
Reply string `json:"reply"`
|
||||||
|
InputSensitive bool `json:"input_sensitive,omitempty"`
|
||||||
|
InputSensitiveType int64 `json:"input_sensitive_type,omitempty"`
|
||||||
|
OutputSensitive bool `json:"output_sensitive"`
|
||||||
|
OutputSensitiveType int64 `json:"output_sensitive_type,omitempty"`
|
||||||
|
Choices []Choice `json:"choices"`
|
||||||
|
Usage *Usage `json:"usage,omitempty"`
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
RequestID string `json:"request_id"`
|
||||||
|
FunctionCall *types.ChatCompletionToolCallsFunction `json:"function_call,omitempty"`
|
||||||
|
MiniMaxBaseResp
|
||||||
|
}
|
||||||
|
|
||||||
|
type Choice struct {
|
||||||
|
Messages []MiniMaxChatMessage `json:"messages"`
|
||||||
|
Index int `json:"index"`
|
||||||
|
FinishReason string `json:"finish_reason"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Usage struct {
|
||||||
|
TotalTokens int `json:"total_tokens"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MiniMaxBaseResp struct {
|
||||||
|
BaseResp BaseResp `json:"base_resp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseResp struct {
|
||||||
|
StatusCode int64 `json:"status_code"`
|
||||||
|
StatusMsg string `json:"status_msg"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MiniMaxEmbeddingRequest struct {
|
||||||
|
Model string `json:"model"`
|
||||||
|
Texts []string `json:"texts"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MiniMaxEmbeddingResponse struct {
|
||||||
|
Vectors []any `json:"vectors"`
|
||||||
|
TotalTokens int `json:"total_tokens"`
|
||||||
|
MiniMaxBaseResp
|
||||||
|
}
|
@ -16,6 +16,7 @@ import (
|
|||||||
"one-api/providers/claude"
|
"one-api/providers/claude"
|
||||||
"one-api/providers/closeai"
|
"one-api/providers/closeai"
|
||||||
"one-api/providers/gemini"
|
"one-api/providers/gemini"
|
||||||
|
"one-api/providers/minimax"
|
||||||
"one-api/providers/openai"
|
"one-api/providers/openai"
|
||||||
"one-api/providers/openaisb"
|
"one-api/providers/openaisb"
|
||||||
"one-api/providers/palm"
|
"one-api/providers/palm"
|
||||||
@ -54,6 +55,7 @@ func init() {
|
|||||||
providerFactories[common.ChannelTypeAzureSpeech] = azurespeech.AzureSpeechProviderFactory{}
|
providerFactories[common.ChannelTypeAzureSpeech] = azurespeech.AzureSpeechProviderFactory{}
|
||||||
providerFactories[common.ChannelTypeGemini] = gemini.GeminiProviderFactory{}
|
providerFactories[common.ChannelTypeGemini] = gemini.GeminiProviderFactory{}
|
||||||
providerFactories[common.ChannelTypeBaichuan] = baichuan.BaichuanProviderFactory{}
|
providerFactories[common.ChannelTypeBaichuan] = baichuan.BaichuanProviderFactory{}
|
||||||
|
providerFactories[common.ChannelTypeMiniMax] = minimax.MiniMaxProviderFactory{}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,6 +71,12 @@ export const CHANNEL_OPTIONS = {
|
|||||||
value: 26,
|
value: 26,
|
||||||
color: 'orange'
|
color: 'orange'
|
||||||
},
|
},
|
||||||
|
27: {
|
||||||
|
key: 27,
|
||||||
|
text: 'MiniMax',
|
||||||
|
value: 27,
|
||||||
|
color: 'orange'
|
||||||
|
},
|
||||||
24: {
|
24: {
|
||||||
key: 24,
|
key: 24,
|
||||||
text: 'Azure Speech',
|
text: 'Azure Speech',
|
||||||
|
@ -166,6 +166,15 @@ const typeConfig = {
|
|||||||
prompt: {
|
prompt: {
|
||||||
test_model: ''
|
test_model: ''
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
27: {
|
||||||
|
input: {
|
||||||
|
models: ['abab5.5-chat', 'abab5.5s-chat', 'abab6-chat', 'embo-01'],
|
||||||
|
test_model: 'abab5.5-chat'
|
||||||
|
},
|
||||||
|
prompt: {
|
||||||
|
key: '按照如下格式输入:APISecret|groupID'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user