合并上游、支持已有渠道获取模型

This commit is contained in:
bubu
2024-05-21 22:21:25 +08:00
7 changed files with 117 additions and 159 deletions

View File

@@ -5,7 +5,7 @@ import (
"strings" "strings"
) )
//from songquanpeng/one-api // from songquanpeng/one-api
const ( const (
USD2RMB = 7.3 // 暂定 1 USD = 7.3 RMB USD2RMB = 7.3 // 暂定 1 USD = 7.3 RMB
USD = 500 // $0.002 = 1 -> $1 = 500 USD = 500 // $0.002 = 1 -> $1 = 500
@@ -289,7 +289,7 @@ func GetCompletionRatio(name string) float64 {
} }
return 4.0 / 3.0 return 4.0 / 3.0
} }
if strings.HasPrefix(name, "gpt-4") && name != "gpt-4-all" && name != "gpt-4-gizmo-*" { if strings.HasPrefix(name, "gpt-4") && !strings.HasSuffix(name, "-all") && !strings.HasSuffix(name, "-gizmo-*") {
if strings.HasPrefix(name, "gpt-4-turbo") || strings.HasSuffix(name, "preview") || strings.HasPrefix(name, "gpt-4o") { if strings.HasPrefix(name, "gpt-4-turbo") || strings.HasSuffix(name, "preview") || strings.HasPrefix(name, "gpt-4o") {
return 3 return 3
} }

View File

@@ -41,10 +41,10 @@ func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) {
return relaycommon.GetFullRequestURL(info.BaseUrl, requestURL, info.ChannelType), nil return relaycommon.GetFullRequestURL(info.BaseUrl, requestURL, info.ChannelType), nil
case common.ChannelTypeMiniMax: case common.ChannelTypeMiniMax:
return minimax.GetRequestURL(info) return minimax.GetRequestURL(info)
//case common.ChannelTypeCustom: case common.ChannelTypeCustom:
// url := info.BaseUrl url := info.BaseUrl
// url = strings.Replace(url, "{model}", info.UpstreamModelName, -1) url = strings.Replace(url, "{model}", info.UpstreamModelName, -1)
// return url, nil return url, nil
default: default:
return relaycommon.GetFullRequestURL(info.BaseUrl, info.RequestURLPath, info.ChannelType), nil return relaycommon.GetFullRequestURL(info.BaseUrl, info.RequestURLPath, info.ChannelType), nil
} }

View File

@@ -310,12 +310,12 @@ const ChannelsTable = () => {
const setChannelFormat = (channels) => { const setChannelFormat = (channels) => {
for (let i = 0; i < channels.length; i++) { for (let i = 0; i < channels.length; i++) {
if (channels[i].type === 8) { // if (channels[i].type === 8) {
showWarning( // showWarning(
'检测到您使用了“自定义渠道”类型请更换为“OpenAI”渠道类型', // '检测到您使用了“自定义渠道”类型请更换为“OpenAI”渠道类型',
); // );
showWarning('下个版本将不再支持“自定义渠道”类型!'); // showWarning('下个版本将不再支持“自定义渠道”类型!');
} // }
channels[i].key = '' + channels[i].id; channels[i].key = '' + channels[i].id;
let test_models = []; let test_models = [];
channels[i].models.split(',').forEach((item, index) => { channels[i].models.split(',').forEach((item, index) => {

View File

@@ -302,6 +302,9 @@ const LogsTable = () => {
let content = '渠道:' + record.channel; let content = '渠道:' + record.channel;
if (record.other !== '') { if (record.other !== '') {
let other = JSON.parse(record.other); let other = JSON.parse(record.other);
if (other === null) {
return <></>
}
if (other.admin_info !== undefined) { if (other.admin_info !== undefined) {
if ( if (
other.admin_info.use_channel !== null && other.admin_info.use_channel !== null &&
@@ -322,7 +325,8 @@ const LogsTable = () => {
title: '详情', title: '详情',
dataIndex: 'content', dataIndex: 'content',
render: (text, record, index) => { render: (text, record, index) => {
if (record.other === '') { let other = JSON.parse(record.other);
if (other == null) {
return ( return (
<Paragraph <Paragraph
ellipsis={{ ellipsis={{
@@ -338,7 +342,6 @@ const LogsTable = () => {
</Paragraph> </Paragraph>
); );
} }
let other = JSON.parse(record.other);
let content = renderModelPrice( let content = renderModelPrice(
record.prompt_tokens, record.prompt_tokens,
record.completion_tokens, record.completion_tokens,

View File

@@ -146,11 +146,11 @@ const ModelPricing = () => {
render: (text, record, index) => { render: (text, record, index) => {
let content = text; let content = text;
if (record.quota_type === 0) { if (record.quota_type === 0) {
let inputRatioPrice = record.model_ratio * 2.0 * record.group_ratio; // 这里的 *2 是因为 1倍率=0.002刀,请勿删除
let inputRatioPrice = record.model_ratio * 2 * record.group_ratio;
let completionRatioPrice = let completionRatioPrice =
record.model_ratio * record.model_ratio *
record.completion_ratio * record.completion_ratio * 2 *
2.0 *
record.group_ratio; record.group_ratio;
content = ( content = (
<> <>

View File

@@ -149,8 +149,9 @@ export function renderModelPrice(
if (completionRatio === undefined) { if (completionRatio === undefined) {
completionRatio = 0; completionRatio = 0;
} }
// 这里的 *2 是因为 1倍率=0.002刀,请勿删除
let inputRatioPrice = modelRatio * 2.0 * groupRatio; let inputRatioPrice = modelRatio * 2.0 * groupRatio;
let completionRatioPrice = modelRatio * completionRatio * 2.0 * groupRatio; let completionRatioPrice = modelRatio * 2.0 * completionRatio * groupRatio;
let price = let price =
(inputTokens / 1000000) * inputRatioPrice + (inputTokens / 1000000) * inputRatioPrice +
(completionTokens / 1000000) * completionRatioPrice; (completionTokens / 1000000) * completionRatioPrice;

View File

@@ -37,8 +37,6 @@ const STATUS_CODE_MAPPING_EXAMPLE = {
400: '500', 400: '500',
}; };
const fetchButtonTips = "1. 新建渠道时请求通过当前浏览器发出2. 编辑已有渠道,请求通过后端服务器发出"
function type2secretPrompt(type) { function type2secretPrompt(type) {
// inputs.type === 15 ? '按照如下格式输入APIKey|SecretKey' : (inputs.type === 18 ? '按照如下格式输入APPID|APISecret|APIKey' : '请输入渠道对应的鉴权密钥') // inputs.type === 15 ? '按照如下格式输入APIKey|SecretKey' : (inputs.type === 18 ? '按照如下格式输入APPID|APISecret|APIKey' : '请输入渠道对应的鉴权密钥')
switch (type) { switch (type) {
@@ -90,55 +88,6 @@ const EditChannel = (props) => {
const [basicModels, setBasicModels] = useState([]); const [basicModels, setBasicModels] = useState([]);
const [fullModels, setFullModels] = useState([]); const [fullModels, setFullModels] = useState([]);
const [customModel, setCustomModel] = useState(''); const [customModel, setCustomModel] = useState('');
const fetchUpstreamModelList = async (name) => {
if (inputs["type"] !== 1) {
showError("仅支持 OpenAI 接口格式")
return;
}
const models = inputs["models"] || []
let err = false;
if (isEdit) {
const res = await API.get("/api/channel/fetch_models/" + channelId)
if (res.data && res.data?.success) {
models.push(...res.data.data)
} else {
err = true
}
} else {
if (!inputs?.["key"]) {
showError("请填写密钥")
return;
}
try {
const host = new URL((inputs["base_url"] || "https://api.openai.com"))
const url = `https://${host.hostname}/v1/models`;
const key = inputs["key"];
const res = await axios.get(url, {
headers: {
'Authorization': `Bearer ${key}`
}
})
if (res.data && res.data?.success) {
models.push(...es.data.data.map((model) => model.id))
} else {
err = true
}
}
catch (error) {
err = true
}
}
if (!err) {
handleInputChange(name, Array.from(new Set(models)));
showSuccess("获取模型列表成功");
} else {
showError('获取模型列表失败');
}
}
const handleInputChange = (name, value) => { const handleInputChange = (name, value) => {
setInputs((inputs) => ({ ...inputs, [name]: value })); setInputs((inputs) => ({ ...inputs, [name]: value }));
if (name === 'type') { if (name === 'type') {
@@ -284,7 +233,7 @@ const EditChannel = (props) => {
fetchModels().then(); fetchModels().then();
fetchGroups().then(); fetchGroups().then();
if (isEdit) { if (isEdit) {
loadChannel().then(() => { }); loadChannel().then(() => {});
} else { } else {
setInputs(originInputs); setInputs(originInputs);
let localModels = getChannelModels(inputs.type); let localModels = getChannelModels(inputs.type);
@@ -354,17 +303,18 @@ const EditChannel = (props) => {
const addCustomModels = () => { const addCustomModels = () => {
if (customModel.trim() === '') return; if (customModel.trim() === '') return;
// 使用逗号分隔字符串,然后去除每个模型名称前后的空格 // 使用逗号分隔字符串,然后去除每个模型名称前后的空格
const modelArray = customModel.split(',').map(model => model.trim()); const modelArray = customModel.split(',').map((model) => model.trim());
let localModels = [...inputs.models]; let localModels = [...inputs.models];
let localModelOptions = [...modelOptions]; let localModelOptions = [...modelOptions];
let hasError = false; let hasError = false;
modelArray.forEach(model => { modelArray.forEach((model) => {
// 检查模型是否已存在,且模型名称非空 // 检查模型是否已存在,且模型名称非空
if (model && !localModels.includes(model)) { if (model && !localModels.includes(model)) {
localModels.push(model); // 添加到模型列表 localModels.push(model); // 添加到模型列表
localModelOptions.push({ // 添加到下拉选项 localModelOptions.push({
// 添加到下拉选项
key: model, key: model,
text: model, text: model,
value: model, value: model,
@@ -486,11 +436,15 @@ const EditChannel = (props) => {
{inputs.type === 8 && ( {inputs.type === 8 && (
<> <>
<div style={{ marginTop: 10 }}> <div style={{ marginTop: 10 }}>
<Typography.Text strong>Base URL</Typography.Text> <Typography.Text strong>
完整的 Base URL支持变量{'{model}'}
</Typography.Text>
</div> </div>
<Input <Input
name='base_url' name='base_url'
placeholder={'请输入自定义渠道的 Base URL'} placeholder={
'请输入完整的URL例如https://api.openai.com/v1/chat/completions'
}
onChange={(value) => { onChange={(value) => {
handleInputChange('base_url', value); handleInputChange('base_url', value);
}} }}