Compare commits
6 Commits
v0.6.11-pr
...
v0.6.11-pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aed090dd55 | ||
|
|
696265774e | ||
|
|
974729426d | ||
|
|
57c1367ec8 | ||
|
|
44233d5c04 | ||
|
|
bf45a955c3 |
@@ -8,4 +8,6 @@ var ModelList = []string{
|
||||
"abab6-chat",
|
||||
"abab5.5-chat",
|
||||
"abab5.5s-chat",
|
||||
"MiniMax-VL-01",
|
||||
"MiniMax-Text-01",
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/songquanpeng/one-api/relay/adaptor/stepfun"
|
||||
"github.com/songquanpeng/one-api/relay/adaptor/togetherai"
|
||||
"github.com/songquanpeng/one-api/relay/adaptor/xai"
|
||||
"github.com/songquanpeng/one-api/relay/adaptor/xunfeiv2"
|
||||
"github.com/songquanpeng/one-api/relay/channeltype"
|
||||
)
|
||||
|
||||
@@ -36,6 +37,7 @@ var CompatibleChannels = []int{
|
||||
channeltype.SiliconFlow,
|
||||
channeltype.XAI,
|
||||
channeltype.BaiduV2,
|
||||
channeltype.XunfeiV2,
|
||||
}
|
||||
|
||||
func GetCompatibleChannelMeta(channelType int) (string, []string) {
|
||||
@@ -72,6 +74,8 @@ func GetCompatibleChannelMeta(channelType int) (string, []string) {
|
||||
return "xai", xai.ModelList
|
||||
case channeltype.BaiduV2:
|
||||
return "baiduv2", baiduv2.ModelList
|
||||
case channeltype.XunfeiV2:
|
||||
return "xunfeiv2", xunfeiv2.ModelList
|
||||
default:
|
||||
return "openai", ModelList
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
package xunfei
|
||||
|
||||
var ModelList = []string{
|
||||
"SparkDesk",
|
||||
"SparkDesk-v1.1",
|
||||
"SparkDesk-v2.1",
|
||||
"SparkDesk-v3.1",
|
||||
"SparkDesk-v3.1-128K",
|
||||
"SparkDesk-v3.5",
|
||||
"SparkDesk-v3.5-32K",
|
||||
"SparkDesk-v4.0",
|
||||
"Spark-Lite",
|
||||
"Spark-Pro",
|
||||
"Spark-Pro-128K",
|
||||
"Spark-Max",
|
||||
"Spark-Max-32K",
|
||||
"Spark-4.0-Ultra",
|
||||
}
|
||||
|
||||
97
relay/adaptor/xunfei/domain.go
Normal file
97
relay/adaptor/xunfei/domain.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package xunfei
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// https://www.xfyun.cn/doc/spark/Web.html#_1-%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E
|
||||
|
||||
//Spark4.0 Ultra 请求地址,对应的domain参数为4.0Ultra:
|
||||
//
|
||||
//wss://spark-api.xf-yun.com/v4.0/chat
|
||||
//Spark Max-32K请求地址,对应的domain参数为max-32k
|
||||
//
|
||||
//wss://spark-api.xf-yun.com/chat/max-32k
|
||||
//Spark Max请求地址,对应的domain参数为generalv3.5
|
||||
//
|
||||
//wss://spark-api.xf-yun.com/v3.5/chat
|
||||
//Spark Pro-128K请求地址,对应的domain参数为pro-128k:
|
||||
//
|
||||
// wss://spark-api.xf-yun.com/chat/pro-128k
|
||||
//Spark Pro请求地址,对应的domain参数为generalv3:
|
||||
//
|
||||
//wss://spark-api.xf-yun.com/v3.1/chat
|
||||
//Spark Lite请求地址,对应的domain参数为lite:
|
||||
//
|
||||
//wss://spark-api.xf-yun.com/v1.1/chat
|
||||
|
||||
// Lite、Pro、Pro-128K、Max、Max-32K和4.0 Ultra
|
||||
|
||||
func parseAPIVersionByModelName(modelName string) string {
|
||||
apiVersion := modelName2APIVersion(modelName)
|
||||
if apiVersion != "" {
|
||||
return apiVersion
|
||||
}
|
||||
|
||||
index := strings.IndexAny(modelName, "-")
|
||||
if index != -1 {
|
||||
return modelName[index+1:]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func modelName2APIVersion(modelName string) string {
|
||||
switch modelName {
|
||||
case "Spark-Lite":
|
||||
return "v1.1"
|
||||
case "Spark-Pro":
|
||||
return "v3.1"
|
||||
case "Spark-Pro-128K":
|
||||
return "v3.1-128K"
|
||||
case "Spark-Max":
|
||||
return "v3.5"
|
||||
case "Spark-Max-32K":
|
||||
return "v3.5-32K"
|
||||
case "Spark-4.0-Ultra":
|
||||
return "v4.0"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// https://www.xfyun.cn/doc/spark/Web.html#_1-%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E
|
||||
func apiVersion2domain(apiVersion string) string {
|
||||
switch apiVersion {
|
||||
case "v1.1":
|
||||
return "lite"
|
||||
case "v2.1":
|
||||
return "generalv2"
|
||||
case "v3.1":
|
||||
return "generalv3"
|
||||
case "v3.1-128K":
|
||||
return "pro-128k"
|
||||
case "v3.5":
|
||||
return "generalv3.5"
|
||||
case "v3.5-32K":
|
||||
return "max-32k"
|
||||
case "v4.0":
|
||||
return "4.0Ultra"
|
||||
}
|
||||
return "general" + apiVersion
|
||||
}
|
||||
|
||||
func getXunfeiAuthUrl(apiVersion string, apiKey string, apiSecret string) (string, string) {
|
||||
var authUrl string
|
||||
domain := apiVersion2domain(apiVersion)
|
||||
switch apiVersion {
|
||||
case "v3.1-128K":
|
||||
authUrl = buildXunfeiAuthUrl(fmt.Sprintf("wss://spark-api.xf-yun.com/chat/pro-128k"), apiKey, apiSecret)
|
||||
break
|
||||
case "v3.5-32K":
|
||||
authUrl = buildXunfeiAuthUrl(fmt.Sprintf("wss://spark-api.xf-yun.com/chat/max-32k"), apiKey, apiSecret)
|
||||
break
|
||||
default:
|
||||
authUrl = buildXunfeiAuthUrl(fmt.Sprintf("wss://spark-api.xf-yun.com/%s/chat", apiVersion), apiKey, apiSecret)
|
||||
}
|
||||
return domain, authUrl
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
|
||||
"github.com/songquanpeng/one-api/common"
|
||||
"github.com/songquanpeng/one-api/common/helper"
|
||||
"github.com/songquanpeng/one-api/common/logger"
|
||||
@@ -270,48 +271,3 @@ func xunfeiMakeRequest(textRequest model.GeneralOpenAIRequest, domain, authUrl,
|
||||
|
||||
return dataChan, stopChan, nil
|
||||
}
|
||||
|
||||
func parseAPIVersionByModelName(modelName string) string {
|
||||
index := strings.IndexAny(modelName, "-")
|
||||
if index != -1 {
|
||||
return modelName[index+1:]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// https://www.xfyun.cn/doc/spark/Web.html#_1-%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E
|
||||
func apiVersion2domain(apiVersion string) string {
|
||||
switch apiVersion {
|
||||
case "v1.1":
|
||||
return "lite"
|
||||
case "v2.1":
|
||||
return "generalv2"
|
||||
case "v3.1":
|
||||
return "generalv3"
|
||||
case "v3.1-128K":
|
||||
return "pro-128k"
|
||||
case "v3.5":
|
||||
return "generalv3.5"
|
||||
case "v3.5-32K":
|
||||
return "max-32k"
|
||||
case "v4.0":
|
||||
return "4.0Ultra"
|
||||
}
|
||||
return "general" + apiVersion
|
||||
}
|
||||
|
||||
func getXunfeiAuthUrl(apiVersion string, apiKey string, apiSecret string) (string, string) {
|
||||
var authUrl string
|
||||
domain := apiVersion2domain(apiVersion)
|
||||
switch apiVersion {
|
||||
case "v3.1-128K":
|
||||
authUrl = buildXunfeiAuthUrl(fmt.Sprintf("wss://spark-api.xf-yun.com/chat/pro-128k"), apiKey, apiSecret)
|
||||
break
|
||||
case "v3.5-32K":
|
||||
authUrl = buildXunfeiAuthUrl(fmt.Sprintf("wss://spark-api.xf-yun.com/chat/max-32k"), apiKey, apiSecret)
|
||||
break
|
||||
default:
|
||||
authUrl = buildXunfeiAuthUrl(fmt.Sprintf("wss://spark-api.xf-yun.com/%s/chat", apiVersion), apiKey, apiSecret)
|
||||
}
|
||||
return domain, authUrl
|
||||
}
|
||||
|
||||
12
relay/adaptor/xunfeiv2/constants.go
Normal file
12
relay/adaptor/xunfeiv2/constants.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package xunfeiv2
|
||||
|
||||
// https://www.xfyun.cn/doc/spark/HTTP%E8%B0%83%E7%94%A8%E6%96%87%E6%A1%A3.html#_3-%E8%AF%B7%E6%B1%82%E8%AF%B4%E6%98%8E
|
||||
|
||||
var ModelList = []string{
|
||||
"lite",
|
||||
"generalv3",
|
||||
"pro-128k",
|
||||
"generalv3.5",
|
||||
"max-32k",
|
||||
"4.0Ultra",
|
||||
}
|
||||
@@ -49,5 +49,6 @@ const (
|
||||
XAI
|
||||
Replicate
|
||||
BaiduV2
|
||||
XunfeiV2
|
||||
Dummy
|
||||
)
|
||||
|
||||
@@ -49,6 +49,7 @@ var ChannelBaseURLs = []string{
|
||||
"https://api.x.ai", // 45
|
||||
"https://api.replicate.com/v1/models/", // 46
|
||||
"https://qianfan.baidubce.com", // 47
|
||||
"https://spark-api-open.xf-yun.com", // 48
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -38,7 +38,7 @@ func RelayTextHelper(c *gin.Context) *model.ErrorWithStatusCode {
|
||||
textRequest.Model, _ = getMappedModelName(textRequest.Model, meta.ModelMapping)
|
||||
meta.ActualModelName = textRequest.Model
|
||||
// set system prompt if not empty
|
||||
systemPromptReset := setSystemPrompt(ctx, textRequest, meta.SystemPrompt)
|
||||
systemPromptReset := setSystemPrompt(ctx, textRequest, meta.ForcedSystemPrompt)
|
||||
// get model ratio & group ratio
|
||||
modelRatio := billingratio.GetModelRatio(textRequest.Model, meta.ChannelType)
|
||||
groupRatio := billingratio.GetGroupRatio(meta.Group)
|
||||
@@ -88,7 +88,11 @@ func RelayTextHelper(c *gin.Context) *model.ErrorWithStatusCode {
|
||||
}
|
||||
|
||||
func getRequestBody(c *gin.Context, meta *meta.Meta, textRequest *model.GeneralOpenAIRequest, adaptor adaptor.Adaptor) (io.Reader, error) {
|
||||
if !config.EnforceIncludeUsage && meta.APIType == apitype.OpenAI && meta.OriginModelName == meta.ActualModelName && meta.ChannelType != channeltype.Baichuan {
|
||||
if !config.EnforceIncludeUsage &&
|
||||
meta.APIType == apitype.OpenAI &&
|
||||
meta.OriginModelName == meta.ActualModelName &&
|
||||
meta.ChannelType != channeltype.Baichuan &&
|
||||
meta.ForcedSystemPrompt == "" {
|
||||
// no need to convert request for openai
|
||||
return c.Request.Body, nil
|
||||
}
|
||||
|
||||
@@ -30,29 +30,29 @@ type Meta struct {
|
||||
// OriginModelName is the model name from the raw user request
|
||||
OriginModelName string
|
||||
// ActualModelName is the model name after mapping
|
||||
ActualModelName string
|
||||
RequestURLPath string
|
||||
PromptTokens int // only for DoResponse
|
||||
SystemPrompt string
|
||||
StartTime time.Time
|
||||
ActualModelName string
|
||||
RequestURLPath string
|
||||
PromptTokens int // only for DoResponse
|
||||
ForcedSystemPrompt string
|
||||
StartTime time.Time
|
||||
}
|
||||
|
||||
func GetByContext(c *gin.Context) *Meta {
|
||||
meta := Meta{
|
||||
Mode: relaymode.GetByPath(c.Request.URL.Path),
|
||||
ChannelType: c.GetInt(ctxkey.Channel),
|
||||
ChannelId: c.GetInt(ctxkey.ChannelId),
|
||||
TokenId: c.GetInt(ctxkey.TokenId),
|
||||
TokenName: c.GetString(ctxkey.TokenName),
|
||||
UserId: c.GetInt(ctxkey.Id),
|
||||
Group: c.GetString(ctxkey.Group),
|
||||
ModelMapping: c.GetStringMapString(ctxkey.ModelMapping),
|
||||
OriginModelName: c.GetString(ctxkey.RequestModel),
|
||||
BaseURL: c.GetString(ctxkey.BaseURL),
|
||||
APIKey: strings.TrimPrefix(c.Request.Header.Get("Authorization"), "Bearer "),
|
||||
RequestURLPath: c.Request.URL.String(),
|
||||
SystemPrompt: c.GetString(ctxkey.SystemPrompt),
|
||||
StartTime: time.Now(),
|
||||
Mode: relaymode.GetByPath(c.Request.URL.Path),
|
||||
ChannelType: c.GetInt(ctxkey.Channel),
|
||||
ChannelId: c.GetInt(ctxkey.ChannelId),
|
||||
TokenId: c.GetInt(ctxkey.TokenId),
|
||||
TokenName: c.GetString(ctxkey.TokenName),
|
||||
UserId: c.GetInt(ctxkey.Id),
|
||||
Group: c.GetString(ctxkey.Group),
|
||||
ModelMapping: c.GetStringMapString(ctxkey.ModelMapping),
|
||||
OriginModelName: c.GetString(ctxkey.RequestModel),
|
||||
BaseURL: c.GetString(ctxkey.BaseURL),
|
||||
APIKey: strings.TrimPrefix(c.Request.Header.Get("Authorization"), "Bearer "),
|
||||
RequestURLPath: c.Request.URL.String(),
|
||||
ForcedSystemPrompt: c.GetString(ctxkey.SystemPrompt),
|
||||
StartTime: time.Now(),
|
||||
}
|
||||
cfg, ok := c.Get(ctxkey.Config)
|
||||
if ok {
|
||||
|
||||
@@ -26,6 +26,7 @@ type GeneralOpenAIRequest struct {
|
||||
Messages []Message `json:"messages,omitempty"`
|
||||
Model string `json:"model,omitempty"`
|
||||
Store *bool `json:"store,omitempty"`
|
||||
ReasoningEffort *string `json:"reasoning_effort,omitempty"`
|
||||
Metadata any `json:"metadata,omitempty"`
|
||||
FrequencyPenalty *float64 `json:"frequency_penalty,omitempty"`
|
||||
LogitBias any `json:"logit_bias,omitempty"`
|
||||
|
||||
@@ -4,6 +4,14 @@ type Usage struct {
|
||||
PromptTokens int `json:"prompt_tokens"`
|
||||
CompletionTokens int `json:"completion_tokens"`
|
||||
TotalTokens int `json:"total_tokens"`
|
||||
|
||||
CompletionTokensDetails *CompletionTokensDetails `json:"completion_tokens_details,omitempty"`
|
||||
}
|
||||
|
||||
type CompletionTokensDetails struct {
|
||||
ReasoningTokens int `json:"reasoning_tokens"`
|
||||
AcceptedPredictionTokens int `json:"accepted_prediction_tokens"`
|
||||
RejectedPredictionTokens int `json:"rejected_prediction_tokens"`
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
|
||||
@@ -1,17 +1,7 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
Button,
|
||||
Dropdown,
|
||||
Form,
|
||||
Input,
|
||||
Label,
|
||||
Message,
|
||||
Pagination,
|
||||
Popup,
|
||||
Table,
|
||||
} from 'semantic-ui-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {Button, Dropdown, Form, Input, Label, Message, Pagination, Popup, Table,} from 'semantic-ui-react';
|
||||
import {Link} from 'react-router-dom';
|
||||
import {
|
||||
API,
|
||||
loadChannelModels,
|
||||
@@ -23,8 +13,8 @@ import {
|
||||
timestamp2string,
|
||||
} from '../helpers';
|
||||
|
||||
import { CHANNEL_OPTIONS, ITEMS_PER_PAGE } from '../constants';
|
||||
import { renderGroup, renderNumber } from '../helpers/render';
|
||||
import {CHANNEL_OPTIONS, ITEMS_PER_PAGE} from '../constants';
|
||||
import {renderGroup, renderNumber} from '../helpers/render';
|
||||
|
||||
function renderTimestamp(timestamp) {
|
||||
return <>{timestamp2string(timestamp)}</>;
|
||||
@@ -93,30 +83,32 @@ const ChannelsTable = () => {
|
||||
const [showPrompt, setShowPrompt] = useState(shouldShowPrompt(promptID));
|
||||
const [showDetail, setShowDetail] = useState(isShowDetail());
|
||||
|
||||
const processChannelData = (channel) => {
|
||||
if (channel.models === '') {
|
||||
channel.models = [];
|
||||
channel.test_model = '';
|
||||
} else {
|
||||
channel.models = channel.models.split(',');
|
||||
if (channel.models.length > 0) {
|
||||
channel.test_model = channel.models[0];
|
||||
}
|
||||
channel.model_options = channel.models.map((model) => {
|
||||
return {
|
||||
key: model,
|
||||
text: model,
|
||||
value: model,
|
||||
};
|
||||
});
|
||||
console.log('channel', channel);
|
||||
}
|
||||
return channel;
|
||||
};
|
||||
|
||||
const loadChannels = async (startIdx) => {
|
||||
const res = await API.get(`/api/channel/?p=${startIdx}`);
|
||||
const { success, message, data } = res.data;
|
||||
const {success, message, data} = res.data;
|
||||
if (success) {
|
||||
let localChannels = data.map((channel) => {
|
||||
if (channel.models === '') {
|
||||
channel.models = [];
|
||||
channel.test_model = '';
|
||||
} else {
|
||||
channel.models = channel.models.split(',');
|
||||
if (channel.models.length > 0) {
|
||||
channel.test_model = channel.models[0];
|
||||
}
|
||||
channel.model_options = channel.models.map((model) => {
|
||||
return {
|
||||
key: model,
|
||||
text: model,
|
||||
value: model,
|
||||
};
|
||||
});
|
||||
console.log('channel', channel);
|
||||
}
|
||||
return channel;
|
||||
});
|
||||
let localChannels = data.map(processChannelData);
|
||||
if (startIdx === 0) {
|
||||
setChannels(localChannels);
|
||||
} else {
|
||||
@@ -301,7 +293,8 @@ const ChannelsTable = () => {
|
||||
const res = await API.get(`/api/channel/search?keyword=${searchKeyword}`);
|
||||
const { success, message, data } = res.data;
|
||||
if (success) {
|
||||
setChannels(data);
|
||||
let localChannels = data.map(processChannelData);
|
||||
setChannels(localChannels);
|
||||
setActivePage(1);
|
||||
} else {
|
||||
showError(message);
|
||||
|
||||
@@ -29,7 +29,20 @@ export const CHANNEL_OPTIONS = [
|
||||
tip: '请前往<a href="https://console.bce.baidu.com/iam/#/iam/apikey/list" target="_blank">此处</a>获取 API Key,注意本渠道仅支持<a target="_blank" href="https://cloud.baidu.com/doc/WENXINWORKSHOP/s/em4tsqo3v">推理服务 V2</a>相关模型',
|
||||
},
|
||||
{key: 17, text: '阿里通义千问', value: 17, color: 'orange'},
|
||||
{key: 18, text: '讯飞星火认知', value: 18, color: 'blue'},
|
||||
{
|
||||
key: 18,
|
||||
text: '讯飞星火认知',
|
||||
value: 18,
|
||||
color: 'blue',
|
||||
tip: '本渠道基于讯飞 WebSocket 版本 API,如需 HTTP 版本,请使用<strong>讯飞星火认知 V2</strong>渠道',
|
||||
},
|
||||
{
|
||||
key: 48,
|
||||
text: '讯飞星火认知 V2',
|
||||
value: 48,
|
||||
color: 'blue',
|
||||
tip: 'HTTP 版本的讯飞接口,前往<a href="https://console.xfyun.cn/services/cbm" target="_blank">此处</a>获取 HTTP 服务接口认证密钥',
|
||||
},
|
||||
{key: 16, text: '智谱 ChatGLM', value: 16, color: 'violet'},
|
||||
{key: 19, text: '360 智脑', value: 19, color: 'blue'},
|
||||
{key: 25, text: 'Moonshot AI', value: 25, color: 'black'},
|
||||
|
||||
Reference in New Issue
Block a user