From 07b2fd58d646817e597bd6edceabb1c12e57ed38 Mon Sep 17 00:00:00 2001
From: Buer <42402987+MartialBE@users.noreply.github.com>
Date: Tue, 28 May 2024 01:22:40 +0800
Subject: [PATCH 01/19] feat: berry theme update & bug fix (#1471)
* feat: load channel models from server
* chore: support AWS Claude/Cloudflare/Coze
* fix: Popup message when copying fails
* chore: Optimize tips
---
web/berry/src/constants/ChannelConstants.js | 30 +-
web/berry/src/constants/SnackbarConstants.js | 42 +-
web/berry/src/utils/common.js | 37 ++
.../AuthForms/ResetPasswordForm.js | 43 +-
.../src/views/Channel/component/EditModal.js | 502 +++++++-----------
.../src/views/Channel/component/NameLabel.js | 27 +-
web/berry/src/views/Channel/index.js | 8 +-
web/berry/src/views/Channel/type/Config.js | 196 ++++---
web/berry/src/views/Profile/index.js | 5 +-
.../views/Redemption/component/TableRow.js | 5 +-
.../src/views/Token/component/TableRow.js | 14 +-
.../src/views/Topup/component/InviteCard.js | 8 +-
12 files changed, 448 insertions(+), 469 deletions(-)
diff --git a/web/berry/src/constants/ChannelConstants.js b/web/berry/src/constants/ChannelConstants.js
index e6b0aed5..589ef1fb 100644
--- a/web/berry/src/constants/ChannelConstants.js
+++ b/web/berry/src/constants/ChannelConstants.js
@@ -11,12 +11,18 @@ export const CHANNEL_OPTIONS = {
value: 14,
color: 'primary'
},
- // 33: {
- // key: 33,
- // text: 'AWS Claude',
- // value: 33,
- // color: 'primary'
- // },
+ 33: {
+ key: 33,
+ text: 'AWS Claude',
+ value: 33,
+ color: 'primary'
+ },
+ 37: {
+ key: 37,
+ text: 'Cloudflare',
+ value: 37,
+ color: 'success'
+ },
3: {
key: 3,
text: 'Azure OpenAI',
@@ -119,12 +125,12 @@ export const CHANNEL_OPTIONS = {
value: 32,
color: 'primary'
},
- // 34: {
- // key: 34,
- // text: 'Coze',
- // value: 34,
- // color: 'primary'
- // },
+ 34: {
+ key: 34,
+ text: 'Coze',
+ value: 34,
+ color: 'primary'
+ },
35: {
key: 35,
text: 'Cohere',
diff --git a/web/berry/src/constants/SnackbarConstants.js b/web/berry/src/constants/SnackbarConstants.js
index 19523da1..05f79231 100644
--- a/web/berry/src/constants/SnackbarConstants.js
+++ b/web/berry/src/constants/SnackbarConstants.js
@@ -1,24 +1,56 @@
+import { closeSnackbar } from 'notistack';
+import { IconX } from '@tabler/icons-react';
+import { IconButton } from '@mui/material';
+const action = (snackbarId) => (
+ <>
+ {
+ closeSnackbar(snackbarId);
+ }}
+ >
+
+
+ >
+);
+
export const snackbarConstants = {
Common: {
ERROR: {
variant: 'error',
- autoHideDuration: 5000
+ autoHideDuration: 5000,
+ preventDuplicate: true,
+ action
},
WARNING: {
variant: 'warning',
- autoHideDuration: 10000
+ autoHideDuration: 10000,
+ preventDuplicate: true,
+ action
},
SUCCESS: {
variant: 'success',
- autoHideDuration: 1500
+ autoHideDuration: 1500,
+ preventDuplicate: true,
+ action
},
INFO: {
variant: 'info',
- autoHideDuration: 3000
+ autoHideDuration: 3000,
+ preventDuplicate: true,
+ action
},
NOTICE: {
variant: 'info',
- autoHideDuration: 7000
+ autoHideDuration: 20000,
+ preventDuplicate: true,
+ action
+ },
+ COPY: {
+ variant: 'copy',
+ persist: true,
+ preventDuplicate: true,
+ allowDownload: true,
+ action
}
},
Mobile: {
diff --git a/web/berry/src/utils/common.js b/web/berry/src/utils/common.js
index 947df3bf..d74d032e 100644
--- a/web/berry/src/utils/common.js
+++ b/web/berry/src/utils/common.js
@@ -193,3 +193,40 @@ export function removeTrailingSlash(url) {
return url;
}
}
+
+let channelModels = undefined;
+export async function loadChannelModels() {
+ const res = await API.get('/api/models');
+ const { success, data } = res.data;
+ if (!success) {
+ return;
+ }
+ channelModels = data;
+ localStorage.setItem('channel_models', JSON.stringify(data));
+}
+
+export function getChannelModels(type) {
+ if (channelModels !== undefined && type in channelModels) {
+ return channelModels[type];
+ }
+ let models = localStorage.getItem('channel_models');
+ if (!models) {
+ return [];
+ }
+ channelModels = JSON.parse(models);
+ if (type in channelModels) {
+ return channelModels[type];
+ }
+ return [];
+}
+
+export function copy(text, name = '') {
+ try {
+ navigator.clipboard.writeText(text);
+ } catch (error) {
+ text = `复制${name}失败,请手动复制:
${text}`;
+ enqueueSnackbar(, getSnackbarOptions('COPY'));
+ return;
+ }
+ showSuccess(`复制${name}成功!`);
+}
diff --git a/web/berry/src/views/Authentication/AuthForms/ResetPasswordForm.js b/web/berry/src/views/Authentication/AuthForms/ResetPasswordForm.js
index eaa8dc95..a9f0f9e3 100644
--- a/web/berry/src/views/Authentication/AuthForms/ResetPasswordForm.js
+++ b/web/berry/src/views/Authentication/AuthForms/ResetPasswordForm.js
@@ -1,22 +1,22 @@
-import { useState, useEffect } from "react";
-import { useSearchParams } from "react-router-dom";
+import { useState, useEffect } from 'react';
+import { useSearchParams } from 'react-router-dom';
// material-ui
-import { Button, Stack, Typography, Alert } from "@mui/material";
+import { Button, Stack, Typography, Alert } from '@mui/material';
// assets
-import { showError, showInfo } from "utils/common";
-import { API } from "utils/api";
+import { showError, copy } from 'utils/common';
+import { API } from 'utils/api';
// ===========================|| FIREBASE - REGISTER ||=========================== //
const ResetPasswordForm = () => {
const [searchParams] = useSearchParams();
const [inputs, setInputs] = useState({
- email: "",
- token: "",
+ email: '',
+ token: ''
});
- const [newPassword, setNewPassword] = useState("");
+ const [newPassword, setNewPassword] = useState('');
const submit = async () => {
const res = await API.post(`/api/user/reset`, inputs);
@@ -24,31 +24,25 @@ const ResetPasswordForm = () => {
if (success) {
let password = res.data.data;
setNewPassword(password);
- navigator.clipboard.writeText(password);
- showInfo(`新密码已复制到剪贴板:${password}`);
+ copy(password, '新密码');
} else {
showError(message);
}
};
useEffect(() => {
- let email = searchParams.get("email");
- let token = searchParams.get("token");
+ let email = searchParams.get('email');
+ let token = searchParams.get('token');
setInputs({
token,
- email,
+ email
});
}, []);
return (
-
+
{!inputs.email || !inputs.token ? (
-
+
无效的链接
) : newPassword ? (
@@ -57,14 +51,7 @@ const ResetPasswordForm = () => {
请登录后及时修改密码
) : (
-
-
+
{matchUpMd ? (
-
+
}>
刷新
diff --git a/web/berry/src/views/Channel/type/Config.js b/web/berry/src/views/Channel/type/Config.js
index 7e42ca8d..88e1ea92 100644
--- a/web/berry/src/views/Channel/type/Config.js
+++ b/web/berry/src/views/Channel/type/Config.js
@@ -1,177 +1,209 @@
const defaultConfig = {
input: {
- name: "",
+ name: '',
type: 1,
- key: "",
- base_url: "",
- other: "",
- model_mapping: "",
+ key: '',
+ base_url: '',
+ other: '',
+ model_mapping: '',
models: [],
- groups: ["default"],
+ groups: ['default'],
+ config: {}
},
inputLabel: {
- name: "渠道名称",
- type: "渠道类型",
- base_url: "渠道API地址",
- key: "密钥",
- other: "其他参数",
- models: "模型",
- model_mapping: "模型映射关系",
- groups: "用户组",
+ name: '渠道名称',
+ type: '渠道类型',
+ base_url: '渠道API地址',
+ key: '密钥',
+ other: '其他参数',
+ models: '模型',
+ model_mapping: '模型映射关系',
+ groups: '用户组',
+ config: null
},
prompt: {
- type: "请选择渠道类型",
- name: "请为渠道命名",
- base_url: "可空,请输入中转API地址,例如通过cloudflare中转",
- key: "请输入渠道对应的鉴权密钥",
- other: "",
- models: "请选择该渠道所支持的模型",
+ type: '请选择渠道类型',
+ name: '请为渠道命名',
+ base_url: '可空,请输入中转API地址,例如通过cloudflare中转',
+ key: '请输入渠道对应的鉴权密钥',
+ other: '',
+ models: '请选择该渠道所支持的模型',
model_mapping:
'请输入要修改的模型映射关系,格式为:api请求模型ID:实际转发给渠道的模型ID,使用JSON数组表示,例如:{"gpt-3.5": "gpt-35"}',
- groups: "请选择该渠道所支持的用户组",
+ groups: '请选择该渠道所支持的用户组',
+ config: null
},
- modelGroup: "openai",
+ modelGroup: 'openai'
};
const typeConfig = {
3: {
inputLabel: {
- base_url: "AZURE_OPENAI_ENDPOINT",
- other: "默认 API 版本",
+ base_url: 'AZURE_OPENAI_ENDPOINT',
+ other: '默认 API 版本'
},
prompt: {
- base_url: "请填写AZURE_OPENAI_ENDPOINT",
- other: "请输入默认API版本,例如:2024-03-01-preview",
- },
+ base_url: '请填写AZURE_OPENAI_ENDPOINT',
+ other: '请输入默认API版本,例如:2024-03-01-preview'
+ }
},
11: {
input: {
- models: ["PaLM-2"],
+ models: ['PaLM-2']
},
- modelGroup: "google palm",
+ modelGroup: 'google palm'
},
14: {
input: {
- models: ["claude-instant-1", "claude-2", "claude-2.0", "claude-2.1"],
+ models: ['claude-instant-1', 'claude-2', 'claude-2.0', 'claude-2.1']
},
- modelGroup: "anthropic",
+ modelGroup: 'anthropic'
},
15: {
input: {
- models: ["ERNIE-Bot", "ERNIE-Bot-turbo", "ERNIE-Bot-4", "Embedding-V1"],
+ models: ['ERNIE-Bot', 'ERNIE-Bot-turbo', 'ERNIE-Bot-4', 'Embedding-V1']
},
prompt: {
- key: "按照如下格式输入:APIKey|SecretKey",
+ key: '按照如下格式输入:APIKey|SecretKey'
},
- modelGroup: "baidu",
+ modelGroup: 'baidu'
},
16: {
input: {
- models: ["glm-4", "glm-4v", "glm-3-turbo", "chatglm_turbo", "chatglm_pro", "chatglm_std", "chatglm_lite"],
+ models: ['glm-4', 'glm-4v', 'glm-3-turbo', 'chatglm_turbo', 'chatglm_pro', 'chatglm_std', 'chatglm_lite']
},
- modelGroup: "zhipu",
+ modelGroup: 'zhipu'
},
17: {
inputLabel: {
- other: "插件参数",
+ other: '插件参数'
},
input: {
- models: [
- "qwen-turbo",
- "qwen-plus",
- "qwen-max",
- "qwen-max-longcontext",
- "text-embedding-v1",
- ],
+ models: ['qwen-turbo', 'qwen-plus', 'qwen-max', 'qwen-max-longcontext', 'text-embedding-v1']
},
prompt: {
- other: "请输入插件参数,即 X-DashScope-Plugin 请求头的取值",
+ other: '请输入插件参数,即 X-DashScope-Plugin 请求头的取值'
},
- modelGroup: "ali",
+ modelGroup: 'ali'
},
18: {
inputLabel: {
- other: "版本号",
+ other: '版本号'
},
input: {
- models: [
- "SparkDesk",
- 'SparkDesk-v1.1',
- 'SparkDesk-v2.1',
- 'SparkDesk-v3.1',
- 'SparkDesk-v3.5'
- ],
+ models: ['SparkDesk', 'SparkDesk-v1.1', 'SparkDesk-v2.1', 'SparkDesk-v3.1', 'SparkDesk-v3.5']
},
prompt: {
- key: "按照如下格式输入:APPID|APISecret|APIKey",
- other: "请输入版本号,例如:v3.1",
+ key: '按照如下格式输入:APPID|APISecret|APIKey',
+ other: '请输入版本号,例如:v3.1'
},
- modelGroup: "xunfei",
+ modelGroup: 'xunfei'
},
19: {
input: {
- models: [
- "360GPT_S2_V9",
- "embedding-bert-512-v1",
- "embedding_s1_v1",
- "semantic_similarity_s1_v1",
- ],
+ models: ['360GPT_S2_V9', 'embedding-bert-512-v1', 'embedding_s1_v1', 'semantic_similarity_s1_v1']
},
- modelGroup: "360",
+ modelGroup: '360'
},
22: {
prompt: {
- key: "按照如下格式输入:APIKey-AppId,例如:fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041",
- },
+ key: '按照如下格式输入:APIKey-AppId,例如:fastgpt-0sp2gtvfdgyi4k30jwlgwf1i-64f335d84283f05518e9e041'
+ }
},
23: {
input: {
- models: ["hunyuan"],
+ models: ['hunyuan']
},
prompt: {
- key: "按照如下格式输入:AppId|SecretId|SecretKey",
+ key: '按照如下格式输入:AppId|SecretId|SecretKey'
},
- modelGroup: "tencent",
+ modelGroup: 'tencent'
},
24: {
inputLabel: {
- other: "版本号",
+ other: '版本号'
},
input: {
- models: ["gemini-pro"],
+ models: ['gemini-pro']
},
prompt: {
- other: "请输入版本号,例如:v1",
+ other: '请输入版本号,例如:v1'
},
- modelGroup: "google gemini",
+ modelGroup: 'google gemini'
},
25: {
input: {
- models: ['moonshot-v1-8k', 'moonshot-v1-32k', 'moonshot-v1-128k'],
+ models: ['moonshot-v1-8k', 'moonshot-v1-32k', 'moonshot-v1-128k']
},
- modelGroup: "moonshot",
+ modelGroup: 'moonshot'
},
26: {
input: {
- models: ['Baichuan2-Turbo', 'Baichuan2-Turbo-192k', 'Baichuan-Text-Embedding'],
+ models: ['Baichuan2-Turbo', 'Baichuan2-Turbo-192k', 'Baichuan-Text-Embedding']
},
- modelGroup: "baichuan",
+ modelGroup: 'baichuan'
},
27: {
input: {
- models: ['abab5.5s-chat', 'abab5.5-chat', 'abab6-chat'],
+ models: ['abab5.5s-chat', 'abab5.5-chat', 'abab6-chat']
},
- modelGroup: "minimax",
+ modelGroup: 'minimax'
},
29: {
- modelGroup: "groq",
+ modelGroup: 'groq'
},
30: {
- modelGroup: "ollama",
+ modelGroup: 'ollama'
},
31: {
- modelGroup: "lingyiwanwu",
+ modelGroup: 'lingyiwanwu'
},
+ 33: {
+ inputLabel: {
+ key: '',
+ config: {
+ region: 'Region',
+ ak: 'Access Key',
+ sk: 'Secret Key'
+ }
+ },
+ prompt: {
+ key: '',
+ config: {
+ region: 'region,e.g. us-west-2',
+ ak: 'AWS IAM Access Key',
+ sk: 'AWS IAM Secret Key'
+ }
+ },
+ modelGroup: 'anthropic'
+ },
+ 37: {
+ inputLabel: {
+ config: {
+ user_id: 'Account ID'
+ }
+ },
+ prompt: {
+ config: {
+ user_id: '请输入 Account ID,例如:d8d7c61dbc334c32d3ced580e4bf42b4'
+ }
+ },
+ modelGroup: 'Cloudflare'
+ },
+ 34: {
+ inputLabel: {
+ config: {
+ user_id: 'User ID'
+ }
+ },
+ prompt: {
+ models: '对于 Coze 而言,模型名称即 Bot ID,你可以添加一个前缀 `bot-`,例如:`bot-123456`',
+ config: {
+ user_id: '生成该密钥的用户 ID'
+ }
+ },
+ modelGroup: 'Coze'
+ }
};
export { defaultConfig, typeConfig };
diff --git a/web/berry/src/views/Profile/index.js b/web/berry/src/views/Profile/index.js
index 483e3141..4705d8af 100644
--- a/web/berry/src/views/Profile/index.js
+++ b/web/berry/src/views/Profile/index.js
@@ -21,7 +21,7 @@ import { IconBrandWechat, IconBrandGithub, IconMail } from '@tabler/icons-react'
import Label from 'ui-component/Label';
import { API } from 'utils/api';
import { showError, showSuccess } from 'utils/common';
-import { onGitHubOAuthClicked, onLarkOAuthClicked } from 'utils/common';
+import { onGitHubOAuthClicked, onLarkOAuthClicked, copy } from 'utils/common';
import * as Yup from 'yup';
import WechatModal from 'views/Authentication/AuthForms/WechatModal';
import { useSelector } from 'react-redux';
@@ -90,8 +90,7 @@ export default function Profile() {
const { success, message, data } = res.data;
if (success) {
setInputs((inputs) => ({ ...inputs, access_token: data }));
- navigator.clipboard.writeText(data);
- showSuccess(`令牌已重置并已复制到剪贴板`);
+ copy(data, '访问令牌');
} else {
showError(message);
}
diff --git a/web/berry/src/views/Redemption/component/TableRow.js b/web/berry/src/views/Redemption/component/TableRow.js
index 68c9a505..380af037 100644
--- a/web/berry/src/views/Redemption/component/TableRow.js
+++ b/web/berry/src/views/Redemption/component/TableRow.js
@@ -18,7 +18,7 @@ import {
import Label from 'ui-component/Label';
import TableSwitch from 'ui-component/Switch';
-import { timestamp2string, renderQuota, showSuccess } from 'utils/common';
+import { timestamp2string, renderQuota, copy } from 'utils/common';
import { IconDotsVertical, IconEdit, IconTrash } from '@tabler/icons-react';
@@ -83,8 +83,7 @@ export default function RedemptionTableRow({ item, manageRedemption, handleOpenM
variant="contained"
color="primary"
onClick={() => {
- navigator.clipboard.writeText(item.key);
- showSuccess('已复制到剪贴板!');
+ copy(item.key, '兑换码');
}}
>
复制
diff --git a/web/berry/src/views/Token/component/TableRow.js b/web/berry/src/views/Token/component/TableRow.js
index 51ab0d4b..6a197e69 100644
--- a/web/berry/src/views/Token/component/TableRow.js
+++ b/web/berry/src/views/Token/component/TableRow.js
@@ -20,7 +20,7 @@ import {
} from '@mui/material';
import TableSwitch from 'ui-component/Switch';
-import { renderQuota, showSuccess, timestamp2string } from 'utils/common';
+import { renderQuota, timestamp2string, copy } from 'utils/common';
import { IconDotsVertical, IconEdit, IconTrash, IconCaretDownFilled } from '@tabler/icons-react';
@@ -141,8 +141,7 @@ export default function TokensTableRow({ item, manageToken, handleOpenModal, set
if (type === 'link') {
window.open(text);
} else {
- navigator.clipboard.writeText(text);
- showSuccess('已复制到剪贴板!');
+ copy(text);
}
handleCloseMenu();
};
@@ -192,7 +191,7 @@ export default function TokensTableRow({ item, manageToken, handleOpenModal, set
id={`switch-${item.id}`}
checked={statusSwitch === 1}
onChange={handleStatus}
- // disabled={statusSwitch !== 1 && statusSwitch !== 2}
+ // disabled={statusSwitch !== 1 && statusSwitch !== 2}
/>
@@ -211,8 +210,7 @@ export default function TokensTableRow({ item, manageToken, handleOpenModal, set
{
- navigator.clipboard.writeText(`sk-${item.key}`);
- showSuccess('已复制到剪贴板!');
+ copy(`sk-${item.key}`);
}}
>
复制
@@ -222,7 +220,9 @@ export default function TokensTableRow({ item, manageToken, handleOpenModal, set
- handleCopy(COPY_OPTIONS[0], 'link')}>聊天
+ handleCopy(COPY_OPTIONS[0], 'link')}>
+ 聊天
+
handleOpenMenu(e, 'link')}>
diff --git a/web/berry/src/views/Topup/component/InviteCard.js b/web/berry/src/views/Topup/component/InviteCard.js
index a95f85e5..73c9670f 100644
--- a/web/berry/src/views/Topup/component/InviteCard.js
+++ b/web/berry/src/views/Topup/component/InviteCard.js
@@ -4,7 +4,7 @@ import SubCard from 'ui-component/cards/SubCard';
import inviteImage from 'assets/images/invite/cwok_casual_19.webp';
import { useState } from 'react';
import { API } from 'utils/api';
-import { showError, showSuccess } from 'utils/common';
+import { showError, copy } from 'utils/common';
const InviteCard = () => {
const theme = useTheme();
@@ -12,8 +12,7 @@ const InviteCard = () => {
const handleInviteUrl = async () => {
if (inviteUl) {
- navigator.clipboard.writeText(inviteUl);
- showSuccess(`邀请链接已复制到剪切板`);
+ copy(inviteUl, '邀请链接');
return;
}
const res = await API.get('/api/user/aff');
@@ -21,8 +20,7 @@ const InviteCard = () => {
if (success) {
let link = `${window.location.origin}/register?aff=${data}`;
setInviteUrl(link);
- navigator.clipboard.writeText(link);
- showSuccess(`邀请链接已复制到剪切板`);
+ copy(link, '邀请链接');
} else {
showError(message);
}
From a9211d66f6717989c634e4de595c84436e2d4ac5 Mon Sep 17 00:00:00 2001
From: Dafei Zhao
Date: Mon, 27 May 2024 13:26:07 -0400
Subject: [PATCH 02/19] fix: fix gpt-4o token encoding (#1446)
---
relay/adaptor/openai/token.go | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/relay/adaptor/openai/token.go b/relay/adaptor/openai/token.go
index bb9c38a9..ddbfad86 100644
--- a/relay/adaptor/openai/token.go
+++ b/relay/adaptor/openai/token.go
@@ -24,6 +24,10 @@ func InitTokenEncoders() {
logger.FatalLog(fmt.Sprintf("failed to get gpt-3.5-turbo token encoder: %s", err.Error()))
}
defaultTokenEncoder = gpt35TokenEncoder
+ gpt4oTokenEncoder, err := tiktoken.EncodingForModel("gpt-4o")
+ if err != nil {
+ logger.FatalLog(fmt.Sprintf("failed to get gpt-4o token encoder: %s", err.Error()))
+ }
gpt4TokenEncoder, err := tiktoken.EncodingForModel("gpt-4")
if err != nil {
logger.FatalLog(fmt.Sprintf("failed to get gpt-4 token encoder: %s", err.Error()))
@@ -31,6 +35,8 @@ func InitTokenEncoders() {
for model := range billingratio.ModelRatio {
if strings.HasPrefix(model, "gpt-3.5") {
tokenEncoderMap[model] = gpt35TokenEncoder
+ } else if strings.HasPrefix(model, "gpt-4o") {
+ tokenEncoderMap[model] = gpt4oTokenEncoder
} else if strings.HasPrefix(model, "gpt-4") {
tokenEncoderMap[model] = gpt4TokenEncoder
} else {
From fa74ba0eaa4e608f128fb0b4af564f67b32565e2 Mon Sep 17 00:00:00 2001
From: Ghostz <137054651+ye4293@users.noreply.github.com>
Date: Tue, 28 May 2024 01:30:51 +0800
Subject: [PATCH 03/19] chore: print user id when relay error happened (#1447)
* add userid when relay error
* chore: update log format
---------
Co-authored-by: JustSong
---
controller/relay.go | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/controller/relay.go b/controller/relay.go
index aba4cd94..5d8ac690 100644
--- a/controller/relay.go
+++ b/controller/relay.go
@@ -4,6 +4,9 @@ import (
"bytes"
"context"
"fmt"
+ "io"
+ "net/http"
+
"github.com/gin-gonic/gin"
"github.com/songquanpeng/one-api/common"
"github.com/songquanpeng/one-api/common/config"
@@ -16,8 +19,6 @@ import (
"github.com/songquanpeng/one-api/relay/controller"
"github.com/songquanpeng/one-api/relay/model"
"github.com/songquanpeng/one-api/relay/relaymode"
- "io"
- "net/http"
)
// https://platform.openai.com/docs/api-reference/chat
@@ -47,6 +48,7 @@ func Relay(c *gin.Context) {
logger.Debugf(ctx, "request body: %s", string(requestBody))
}
channelId := c.GetInt(ctxkey.ChannelId)
+ userId := c.GetInt("id")
bizErr := relayHelper(c, relayMode)
if bizErr == nil {
monitor.Emit(channelId, true)
@@ -56,7 +58,7 @@ func Relay(c *gin.Context) {
channelName := c.GetString(ctxkey.ChannelName)
group := c.GetString(ctxkey.Group)
originalModel := c.GetString(ctxkey.OriginalModel)
- go processChannelRelayError(ctx, channelId, channelName, bizErr)
+ go processChannelRelayError(ctx, userId, channelId, channelName, bizErr)
requestId := c.GetString(helper.RequestIdKey)
retryTimes := config.RetryTimes
if !shouldRetry(c, bizErr.StatusCode) {
@@ -83,7 +85,7 @@ func Relay(c *gin.Context) {
channelId := c.GetInt(ctxkey.ChannelId)
lastFailedChannelId = channelId
channelName := c.GetString(ctxkey.ChannelName)
- go processChannelRelayError(ctx, channelId, channelName, bizErr)
+ go processChannelRelayError(ctx, userId, channelId, channelName, bizErr)
}
if bizErr != nil {
if bizErr.StatusCode == http.StatusTooManyRequests {
@@ -115,8 +117,8 @@ func shouldRetry(c *gin.Context, statusCode int) bool {
return true
}
-func processChannelRelayError(ctx context.Context, channelId int, channelName string, err *model.ErrorWithStatusCode) {
- logger.Errorf(ctx, "relay error (channel #%d): %s", channelId, err.Message)
+func processChannelRelayError(ctx context.Context, userId int, channelId int, channelName string, err *model.ErrorWithStatusCode) {
+ logger.Errorf(ctx, "relay error (channel id %d, user id: %d): %s", channelId, userId, err.Message)
// https://platform.openai.com/docs/guides/error-codes/api-errors
if monitor.ShouldDisableChannel(&err.Error, err.StatusCode) {
monitor.DisableChannel(channelId, channelName, err.Message)
From 3be28da57b1bc466cd67b037ab38739032af80d6 Mon Sep 17 00:00:00 2001
From: fatwang2 <134143178+fatwang2@users.noreply.github.com>
Date: Tue, 28 May 2024 01:31:08 +0800
Subject: [PATCH 04/19] Update package.json (#1465)
---
web/berry/package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/web/berry/package.json b/web/berry/package.json
index 2edb2355..f8265ef7 100644
--- a/web/berry/package.json
+++ b/web/berry/package.json
@@ -26,7 +26,7 @@
"notistack": "^3.0.1",
"prop-types": "^15.8.1",
"react": "^18.2.0",
- "react-apexcharts": "^1.4.0",
+ "react-apexcharts": "1.4.0",
"react-device-detect": "^2.2.2",
"react-dom": "^18.2.0",
"react-perfect-scrollbar": "^1.5.8",
From 332c8db0b31ef117a5ea090d2875f15111da3a27 Mon Sep 17 00:00:00 2001
From: Mo
Date: Tue, 28 May 2024 01:32:57 +0800
Subject: [PATCH 05/19] fix: add prefixes to image models to solve the problem
of duplicate models (#1469)
* Add prefixes to image models to solve the problem of duplicate models
* Fix the issue that response_format is not set, causing the b64_json parameter to be ignored.
---
relay/billing/ratio/image.go | 5 +++++
relay/controller/image.go | 7 ++++++-
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/relay/billing/ratio/image.go b/relay/billing/ratio/image.go
index 5a29cddc..ced0c667 100644
--- a/relay/billing/ratio/image.go
+++ b/relay/billing/ratio/image.go
@@ -49,3 +49,8 @@ var ImagePromptLengthLimitations = map[string]int{
"wanx-v1": 4000,
"cogview-3": 833,
}
+
+var ImageOriginModelName = map[string]string{
+ "ali-stable-diffusion-xl": "stable-diffusion-xl",
+ "ali-stable-diffusion-v1.5": "stable-diffusion-v1.5",
+}
diff --git a/relay/controller/image.go b/relay/controller/image.go
index 6620bef5..691c7c0e 100644
--- a/relay/controller/image.go
+++ b/relay/controller/image.go
@@ -55,6 +55,11 @@ func RelayImageHelper(c *gin.Context, relayMode int) *relaymodel.ErrorWithStatus
return openai.ErrorWrapper(err, "get_image_cost_ratio_failed", http.StatusInternalServerError)
}
+ imageModel := imageRequest.Model
+ // Convert the original image model
+ imageRequest.Model, _ = getMappedModelName(imageRequest.Model, billingratio.ImageOriginModelName)
+ c.Set("response_format", imageRequest.ResponseFormat)
+
var requestBody io.Reader
if isModelMapped || meta.ChannelType == channeltype.Azure { // make Azure channel request body
jsonStr, err := json.Marshal(imageRequest)
@@ -89,7 +94,7 @@ func RelayImageHelper(c *gin.Context, relayMode int) *relaymodel.ErrorWithStatus
requestBody = bytes.NewBuffer(jsonStr)
}
- modelRatio := billingratio.GetModelRatio(imageRequest.Model)
+ modelRatio := billingratio.GetModelRatio(imageModel)
groupRatio := billingratio.GetGroupRatio(meta.Group)
ratio := modelRatio * groupRatio
userQuota, err := model.CacheGetUserQuota(ctx, meta.UserId)
From b53e00a9b375b44c7c6b54ad2a2e7c4b868823f9 Mon Sep 17 00:00:00 2001
From: carey036 <45777074+carey036@users.noreply.github.com>
Date: Tue, 28 May 2024 01:44:38 +0800
Subject: [PATCH 06/19] feat: generate default token after register (#1401)
* feat: generate default token after register
* chore: use go routine to create default token for new user
---------
Co-authored-by: JustSong
---
controller/user.go | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/controller/user.go b/controller/user.go
index af90acf6..9ab37b5a 100644
--- a/controller/user.go
+++ b/controller/user.go
@@ -6,6 +6,8 @@ import (
"github.com/songquanpeng/one-api/common"
"github.com/songquanpeng/one-api/common/config"
"github.com/songquanpeng/one-api/common/ctxkey"
+ "github.com/songquanpeng/one-api/common/helper"
+ "github.com/songquanpeng/one-api/common/logger"
"github.com/songquanpeng/one-api/common/random"
"github.com/songquanpeng/one-api/model"
"net/http"
@@ -109,6 +111,7 @@ func Logout(c *gin.Context) {
}
func Register(c *gin.Context) {
+ ctx := c.Request.Context()
if !config.RegisterEnabled {
c.JSON(http.StatusOK, gin.H{
"message": "管理员关闭了新用户注册",
@@ -173,6 +176,28 @@ func Register(c *gin.Context) {
})
return
}
+ go func() {
+ err := user.ValidateAndFill()
+ if err != nil {
+ logger.Errorf(ctx, "user.ValidateAndFill failed: %w", err)
+ return
+ }
+ cleanToken := model.Token{
+ UserId: user.Id,
+ Name: "default",
+ Key: random.GenerateKey(),
+ CreatedTime: helper.GetTimestamp(),
+ AccessedTime: helper.GetTimestamp(),
+ ExpiredTime: -1,
+ RemainQuota: -1,
+ UnlimitedQuota: true,
+ }
+ err = cleanToken.Insert()
+ if err != nil {
+ logger.Errorf(ctx, "cleanToken.Insert failed: %w", err)
+ return
+ }
+ }()
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "",
From ceea4c6d4a3de6970bab4381316f4cd19c6e7b28 Mon Sep 17 00:00:00 2001
From: JustSong
Date: Wed, 29 May 2024 01:14:00 +0800
Subject: [PATCH 07/19] feat: support user content download proxy & relay proxy
now
---
README.md | 19 ++++++-----
common/client/init.go | 60 +++++++++++++++++++++++++++++++++++
common/config/config.go | 4 +++
common/image/image.go | 5 +--
controller/channel-billing.go | 2 +-
main.go | 2 ++
relay/adaptor/baidu/main.go | 2 +-
relay/adaptor/common.go | 2 +-
relay/client/init.go | 24 --------------
relay/controller/audio.go | 2 +-
10 files changed, 84 insertions(+), 38 deletions(-)
create mode 100644 common/client/init.go
delete mode 100644 relay/client/init.go
diff --git a/README.md b/README.md
index 40f6e4e0..167fe5ba 100644
--- a/README.md
+++ b/README.md
@@ -384,14 +384,17 @@ graph LR
+ `TIKTOKEN_CACHE_DIR`:默认程序启动时会联网下载一些通用的词元的编码,如:`gpt-3.5-turbo`,在一些网络环境不稳定,或者离线情况,可能会导致启动有问题,可以配置此目录缓存数据,可迁移到离线环境。
+ `DATA_GYM_CACHE_DIR`:目前该配置作用与 `TIKTOKEN_CACHE_DIR` 一致,但是优先级没有它高。
17. `RELAY_TIMEOUT`:中继超时设置,单位为秒,默认不设置超时时间。
-18. `SQLITE_BUSY_TIMEOUT`:SQLite 锁等待超时设置,单位为毫秒,默认 `3000`。
-19. `GEMINI_SAFETY_SETTING`:Gemini 的安全设置,默认 `BLOCK_NONE`。
-20. `GEMINI_VERSION`:One API 所使用的 Gemini 版本,默认为 `v1`。
-21. `THEME`:系统的主题设置,默认为 `default`,具体可选值参考[此处](./web/README.md)。
-22. `ENABLE_METRIC`:是否根据请求成功率禁用渠道,默认不开启,可选值为 `true` 和 `false`。
-23. `METRIC_QUEUE_SIZE`:请求成功率统计队列大小,默认为 `10`。
-24. `METRIC_SUCCESS_RATE_THRESHOLD`:请求成功率阈值,默认为 `0.8`。
-25. `INITIAL_ROOT_TOKEN`:如果设置了该值,则在系统首次启动时会自动创建一个值为该环境变量值的 root 用户令牌。
+18. `RELAY_PROXY`:设置后使用该代理来请求 API。
+19. `USER_CONTENT_REQUEST_TIMEOUT`:用户上传内容下载超时时间,单位为秒。
+20. `USER_CONTENT_REQUEST_PROXY`:设置后使用该代理来请求用户上传的内容,例如图片。
+21. `SQLITE_BUSY_TIMEOUT`:SQLite 锁等待超时设置,单位为毫秒,默认 `3000`。
+22. `GEMINI_SAFETY_SETTING`:Gemini 的安全设置,默认 `BLOCK_NONE`。
+23. `GEMINI_VERSION`:One API 所使用的 Gemini 版本,默认为 `v1`。
+24. `THEME`:系统的主题设置,默认为 `default`,具体可选值参考[此处](./web/README.md)。
+25. `ENABLE_METRIC`:是否根据请求成功率禁用渠道,默认不开启,可选值为 `true` 和 `false`。
+26. `METRIC_QUEUE_SIZE`:请求成功率统计队列大小,默认为 `10`。
+27. `METRIC_SUCCESS_RATE_THRESHOLD`:请求成功率阈值,默认为 `0.8`。
+28. `INITIAL_ROOT_TOKEN`:如果设置了该值,则在系统首次启动时会自动创建一个值为该环境变量值的 root 用户令牌。
### 命令行参数
1. `--port `: 指定服务器监听的端口号,默认为 `3000`。
diff --git a/common/client/init.go b/common/client/init.go
new file mode 100644
index 00000000..f803cbf8
--- /dev/null
+++ b/common/client/init.go
@@ -0,0 +1,60 @@
+package client
+
+import (
+ "fmt"
+ "github.com/songquanpeng/one-api/common/config"
+ "github.com/songquanpeng/one-api/common/logger"
+ "net/http"
+ "net/url"
+ "time"
+)
+
+var HTTPClient *http.Client
+var ImpatientHTTPClient *http.Client
+var UserContentRequestHTTPClient *http.Client
+
+func Init() {
+ if config.UserContentRequestProxy != "" {
+ logger.SysLog(fmt.Sprintf("using %s as proxy to fetch user content", config.UserContentRequestProxy))
+ proxyURL, err := url.Parse(config.UserContentRequestProxy)
+ if err != nil {
+ logger.FatalLog(fmt.Sprintf("USER_CONTENT_REQUEST_PROXY set but invalid: %s", config.UserContentRequestProxy))
+ }
+ transport := &http.Transport{
+ Proxy: http.ProxyURL(proxyURL),
+ }
+ UserContentRequestHTTPClient = &http.Client{
+ Transport: transport,
+ Timeout: time.Second * time.Duration(config.UserContentRequestTimeout),
+ }
+ } else {
+ UserContentRequestHTTPClient = &http.Client{}
+ }
+ var transport http.RoundTripper
+ if config.RelayProxy != "" {
+ logger.SysLog(fmt.Sprintf("using %s as api relay proxy", config.RelayProxy))
+ proxyURL, err := url.Parse(config.RelayProxy)
+ if err != nil {
+ logger.FatalLog(fmt.Sprintf("USER_CONTENT_REQUEST_PROXY set but invalid: %s", config.UserContentRequestProxy))
+ }
+ transport = &http.Transport{
+ Proxy: http.ProxyURL(proxyURL),
+ }
+ }
+
+ if config.RelayTimeout == 0 {
+ HTTPClient = &http.Client{
+ Transport: transport,
+ }
+ } else {
+ HTTPClient = &http.Client{
+ Timeout: time.Duration(config.RelayTimeout) * time.Second,
+ Transport: transport,
+ }
+ }
+
+ ImpatientHTTPClient = &http.Client{
+ Timeout: 5 * time.Second,
+ Transport: transport,
+ }
+}
diff --git a/common/config/config.go b/common/config/config.go
index 0864d844..539eeef4 100644
--- a/common/config/config.go
+++ b/common/config/config.go
@@ -144,3 +144,7 @@ var MetricFailChanSize = env.Int("METRIC_FAIL_CHAN_SIZE", 128)
var InitialRootToken = os.Getenv("INITIAL_ROOT_TOKEN")
var GeminiVersion = env.String("GEMINI_VERSION", "v1")
+
+var RelayProxy = env.String("RELAY_PROXY", "")
+var UserContentRequestProxy = env.String("USER_CONTENT_REQUEST_PROXY", "")
+var UserContentRequestTimeout = env.Int("USER_CONTENT_REQUEST_TIMEOUT", 30)
diff --git a/common/image/image.go b/common/image/image.go
index 12f0adff..beebd0c6 100644
--- a/common/image/image.go
+++ b/common/image/image.go
@@ -3,6 +3,7 @@ package image
import (
"bytes"
"encoding/base64"
+ "github.com/songquanpeng/one-api/common/client"
"image"
_ "image/gif"
_ "image/jpeg"
@@ -19,7 +20,7 @@ import (
var dataURLPattern = regexp.MustCompile(`data:image/([^;]+);base64,(.*)`)
func IsImageUrl(url string) (bool, error) {
- resp, err := http.Head(url)
+ resp, err := client.UserContentRequestHTTPClient.Head(url)
if err != nil {
return false, err
}
@@ -34,7 +35,7 @@ func GetImageSizeFromUrl(url string) (width int, height int, err error) {
if !isImage {
return
}
- resp, err := http.Get(url)
+ resp, err := client.UserContentRequestHTTPClient.Get(url)
if err != nil {
return
}
diff --git a/controller/channel-billing.go b/controller/channel-billing.go
index b7ac61fd..53592744 100644
--- a/controller/channel-billing.go
+++ b/controller/channel-billing.go
@@ -4,12 +4,12 @@ import (
"encoding/json"
"errors"
"fmt"
+ "github.com/songquanpeng/one-api/common/client"
"github.com/songquanpeng/one-api/common/config"
"github.com/songquanpeng/one-api/common/logger"
"github.com/songquanpeng/one-api/model"
"github.com/songquanpeng/one-api/monitor"
"github.com/songquanpeng/one-api/relay/channeltype"
- "github.com/songquanpeng/one-api/relay/client"
"io"
"net/http"
"strconv"
diff --git a/main.go b/main.go
index bdcdcd61..eb6f368c 100644
--- a/main.go
+++ b/main.go
@@ -7,6 +7,7 @@ import (
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
"github.com/songquanpeng/one-api/common"
+ "github.com/songquanpeng/one-api/common/client"
"github.com/songquanpeng/one-api/common/config"
"github.com/songquanpeng/one-api/common/logger"
"github.com/songquanpeng/one-api/controller"
@@ -94,6 +95,7 @@ func main() {
logger.SysLog("metric enabled, will disable channel if too much request failed")
}
openai.InitTokenEncoders()
+ client.Init()
// Initialize HTTP server
server := gin.New()
diff --git a/relay/adaptor/baidu/main.go b/relay/adaptor/baidu/main.go
index 6df5ce84..b816e0f4 100644
--- a/relay/adaptor/baidu/main.go
+++ b/relay/adaptor/baidu/main.go
@@ -7,9 +7,9 @@ import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/songquanpeng/one-api/common"
+ "github.com/songquanpeng/one-api/common/client"
"github.com/songquanpeng/one-api/common/logger"
"github.com/songquanpeng/one-api/relay/adaptor/openai"
- "github.com/songquanpeng/one-api/relay/client"
"github.com/songquanpeng/one-api/relay/constant"
"github.com/songquanpeng/one-api/relay/model"
"io"
diff --git a/relay/adaptor/common.go b/relay/adaptor/common.go
index 82a5160e..8953d7a3 100644
--- a/relay/adaptor/common.go
+++ b/relay/adaptor/common.go
@@ -4,7 +4,7 @@ import (
"errors"
"fmt"
"github.com/gin-gonic/gin"
- "github.com/songquanpeng/one-api/relay/client"
+ "github.com/songquanpeng/one-api/common/client"
"github.com/songquanpeng/one-api/relay/meta"
"io"
"net/http"
diff --git a/relay/client/init.go b/relay/client/init.go
deleted file mode 100644
index 4b59cba7..00000000
--- a/relay/client/init.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package client
-
-import (
- "github.com/songquanpeng/one-api/common/config"
- "net/http"
- "time"
-)
-
-var HTTPClient *http.Client
-var ImpatientHTTPClient *http.Client
-
-func init() {
- if config.RelayTimeout == 0 {
- HTTPClient = &http.Client{}
- } else {
- HTTPClient = &http.Client{
- Timeout: time.Duration(config.RelayTimeout) * time.Second,
- }
- }
-
- ImpatientHTTPClient = &http.Client{
- Timeout: 5 * time.Second,
- }
-}
diff --git a/relay/controller/audio.go b/relay/controller/audio.go
index 15e74290..8f9708d0 100644
--- a/relay/controller/audio.go
+++ b/relay/controller/audio.go
@@ -9,6 +9,7 @@ import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/songquanpeng/one-api/common"
+ "github.com/songquanpeng/one-api/common/client"
"github.com/songquanpeng/one-api/common/config"
"github.com/songquanpeng/one-api/common/ctxkey"
"github.com/songquanpeng/one-api/common/logger"
@@ -17,7 +18,6 @@ import (
"github.com/songquanpeng/one-api/relay/billing"
billingratio "github.com/songquanpeng/one-api/relay/billing/ratio"
"github.com/songquanpeng/one-api/relay/channeltype"
- "github.com/songquanpeng/one-api/relay/client"
"github.com/songquanpeng/one-api/relay/meta"
relaymodel "github.com/songquanpeng/one-api/relay/model"
"github.com/songquanpeng/one-api/relay/relaymode"
From 9321427c6ef4d703f4e315c15d334c88359ec8e3 Mon Sep 17 00:00:00 2001
From: Wei Tingjiang
Date: Wed, 29 May 2024 01:17:32 +0800
Subject: [PATCH 08/19] feat: support gemini embeddings
(text-embedding-004,embedding-001) (#1475)
* Refactor Gemini Adaptor to Support Embeddings
* Add new models to ModelList
---
relay/adaptor/gemini/adaptor.go | 26 +++++++++--
relay/adaptor/gemini/constants.go | 2 +-
relay/adaptor/gemini/main.go | 76 +++++++++++++++++++++++++++++++
relay/adaptor/gemini/model.go | 27 +++++++++++
4 files changed, 127 insertions(+), 4 deletions(-)
diff --git a/relay/adaptor/gemini/adaptor.go b/relay/adaptor/gemini/adaptor.go
index a4dcae93..12f48c71 100644
--- a/relay/adaptor/gemini/adaptor.go
+++ b/relay/adaptor/gemini/adaptor.go
@@ -13,6 +13,7 @@ import (
"github.com/songquanpeng/one-api/relay/adaptor/openai"
"github.com/songquanpeng/one-api/relay/meta"
"github.com/songquanpeng/one-api/relay/model"
+ "github.com/songquanpeng/one-api/relay/relaymode"
)
type Adaptor struct {
@@ -24,7 +25,14 @@ func (a *Adaptor) Init(meta *meta.Meta) {
func (a *Adaptor) GetRequestURL(meta *meta.Meta) (string, error) {
version := helper.AssignOrDefault(meta.Config.APIVersion, config.GeminiVersion)
- action := "generateContent"
+ action := ""
+ switch meta.Mode {
+ case relaymode.Embeddings:
+ action = "batchEmbedContents"
+ default:
+ action = "generateContent"
+ }
+
if meta.IsStream {
action = "streamGenerateContent?alt=sse"
}
@@ -41,7 +49,14 @@ func (a *Adaptor) ConvertRequest(c *gin.Context, relayMode int, request *model.G
if request == nil {
return nil, errors.New("request is nil")
}
- return ConvertRequest(*request), nil
+ switch relayMode {
+ case relaymode.Embeddings:
+ geminiEmbeddingRequest := ConvertEmbeddingRequest(*request)
+ return geminiEmbeddingRequest, nil
+ default:
+ geminiRequest := ConvertRequest(*request)
+ return geminiRequest, nil
+ }
}
func (a *Adaptor) ConvertImageRequest(request *model.ImageRequest) (any, error) {
@@ -61,7 +76,12 @@ func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, meta *meta.Met
err, responseText = StreamHandler(c, resp)
usage = openai.ResponseText2Usage(responseText, meta.ActualModelName, meta.PromptTokens)
} else {
- err, usage = Handler(c, resp, meta.PromptTokens, meta.ActualModelName)
+ switch meta.Mode {
+ case relaymode.Embeddings:
+ err, usage = EmbeddingHandler(c, resp)
+ default:
+ err, usage = Handler(c, resp, meta.PromptTokens, meta.ActualModelName)
+ }
}
return
}
diff --git a/relay/adaptor/gemini/constants.go b/relay/adaptor/gemini/constants.go
index 32e7c240..f65e6bfc 100644
--- a/relay/adaptor/gemini/constants.go
+++ b/relay/adaptor/gemini/constants.go
@@ -4,5 +4,5 @@ package gemini
var ModelList = []string{
"gemini-pro", "gemini-1.0-pro-001", "gemini-1.5-pro",
- "gemini-pro-vision", "gemini-1.0-pro-vision-001",
+ "gemini-pro-vision", "gemini-1.0-pro-vision-001", "embedding-001", "text-embedding-004",
}
diff --git a/relay/adaptor/gemini/main.go b/relay/adaptor/gemini/main.go
index faccc4cb..534b2708 100644
--- a/relay/adaptor/gemini/main.go
+++ b/relay/adaptor/gemini/main.go
@@ -134,6 +134,29 @@ func ConvertRequest(textRequest model.GeneralOpenAIRequest) *ChatRequest {
return &geminiRequest
}
+func ConvertEmbeddingRequest(request model.GeneralOpenAIRequest) *BatchEmbeddingRequest {
+ inputs := request.ParseInput()
+ requests := make([]EmbeddingRequest, len(inputs))
+ model := fmt.Sprintf("models/%s", request.Model)
+
+ for i, input := range inputs {
+ requests[i] = EmbeddingRequest{
+ Model: model,
+ Content: ChatContent{
+ Parts: []Part{
+ {
+ Text: input,
+ },
+ },
+ },
+ }
+ }
+
+ return &BatchEmbeddingRequest{
+ Requests: requests,
+ }
+}
+
type ChatResponse struct {
Candidates []ChatCandidate `json:"candidates"`
PromptFeedback ChatPromptFeedback `json:"promptFeedback"`
@@ -230,6 +253,23 @@ func streamResponseGeminiChat2OpenAI(geminiResponse *ChatResponse) *openai.ChatC
return &response
}
+func embeddingResponseGemini2OpenAI(response *EmbeddingResponse) *openai.EmbeddingResponse {
+ openAIEmbeddingResponse := openai.EmbeddingResponse{
+ Object: "list",
+ Data: make([]openai.EmbeddingResponseItem, 0, len(response.Embeddings)),
+ Model: "gemini-embedding",
+ Usage: model.Usage{TotalTokens: 0},
+ }
+ for _, item := range response.Embeddings {
+ openAIEmbeddingResponse.Data = append(openAIEmbeddingResponse.Data, openai.EmbeddingResponseItem{
+ Object: `embedding`,
+ Index: 0,
+ Embedding: item.Values,
+ })
+ }
+ return &openAIEmbeddingResponse
+}
+
func StreamHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusCode, string) {
responseText := ""
scanner := bufio.NewScanner(resp.Body)
@@ -337,3 +377,39 @@ func Handler(c *gin.Context, resp *http.Response, promptTokens int, modelName st
_, err = c.Writer.Write(jsonResponse)
return nil, &usage
}
+
+func EmbeddingHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusCode, *model.Usage) {
+ var geminiEmbeddingResponse EmbeddingResponse
+ responseBody, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return openai.ErrorWrapper(err, "read_response_body_failed", http.StatusInternalServerError), nil
+ }
+ err = resp.Body.Close()
+ if err != nil {
+ return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil
+ }
+ err = json.Unmarshal(responseBody, &geminiEmbeddingResponse)
+ if err != nil {
+ return openai.ErrorWrapper(err, "unmarshal_response_body_failed", http.StatusInternalServerError), nil
+ }
+ if geminiEmbeddingResponse.Error != nil {
+ return &model.ErrorWithStatusCode{
+ Error: model.Error{
+ Message: geminiEmbeddingResponse.Error.Message,
+ Type: "gemini_error",
+ Param: "",
+ Code: geminiEmbeddingResponse.Error.Code,
+ },
+ StatusCode: resp.StatusCode,
+ }, nil
+ }
+ fullTextResponse := embeddingResponseGemini2OpenAI(&geminiEmbeddingResponse)
+ jsonResponse, err := json.Marshal(fullTextResponse)
+ if err != nil {
+ return openai.ErrorWrapper(err, "marshal_response_body_failed", http.StatusInternalServerError), nil
+ }
+ c.Writer.Header().Set("Content-Type", "application/json")
+ c.Writer.WriteHeader(resp.StatusCode)
+ _, err = c.Writer.Write(jsonResponse)
+ return nil, &fullTextResponse.Usage
+}
diff --git a/relay/adaptor/gemini/model.go b/relay/adaptor/gemini/model.go
index 47b74fbc..f7179ea4 100644
--- a/relay/adaptor/gemini/model.go
+++ b/relay/adaptor/gemini/model.go
@@ -7,6 +7,33 @@ type ChatRequest struct {
Tools []ChatTools `json:"tools,omitempty"`
}
+type EmbeddingRequest struct {
+ Model string `json:"model"`
+ Content ChatContent `json:"content"`
+ TaskType string `json:"taskType,omitempty"`
+ Title string `json:"title,omitempty"`
+ OutputDimensionality int `json:"outputDimensionality,omitempty"`
+}
+
+type BatchEmbeddingRequest struct {
+ Requests []EmbeddingRequest `json:"requests"`
+}
+
+type EmbeddingData struct {
+ Values []float64 `json:"values"`
+}
+
+type EmbeddingResponse struct {
+ Embeddings []EmbeddingData `json:"embeddings"`
+ Error *Error `json:"error,omitempty"`
+}
+
+type Error struct {
+ Code int `json:"code,omitempty"`
+ Message string `json:"message,omitempty"`
+ Status string `json:"status,omitempty"`
+}
+
type InlineData struct {
MimeType string `json:"mimeType"`
Data string `json:"data"`
From 98669d5d480e3c7eff45b4b5e5602440141d91a1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=8F=96=E6=A2=A6=E4=B8=BA=E9=A5=AE?=
Date: Sat, 8 Jun 2024 13:26:26 +0800
Subject: [PATCH 09/19] feat: add support for bytedance's doubao (#1438)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* 增加豆包大模型支持
* chore: update channel options & add prompt
---------
Co-authored-by: 康龙彪
Co-authored-by: JustSong
---
README.md | 2 +-
relay/adaptor/doubao/constants.go | 13 +++++++++++++
relay/adaptor/doubao/main.go | 14 ++++++++++++++
relay/adaptor/openai/adaptor.go | 3 +++
relay/adaptor/openai/compatible.go | 4 ++++
relay/channeltype/define.go | 2 +-
relay/channeltype/url.go | 1 +
web/berry/src/constants/ChannelConstants.js | 6 ++++++
web/default/src/constants/channel.constants.js | 1 +
web/default/src/pages/Channel/EditChannel.js | 7 +++++++
10 files changed, 51 insertions(+), 2 deletions(-)
create mode 100644 relay/adaptor/doubao/constants.go
create mode 100644 relay/adaptor/doubao/main.go
diff --git a/README.md b/README.md
index 167fe5ba..a1a24e0a 100644
--- a/README.md
+++ b/README.md
@@ -68,6 +68,7 @@ _✨ 通过标准的 OpenAI API 格式访问所有的大模型,开箱即用
+ [x] [Anthropic Claude 系列模型](https://anthropic.com) (支持 AWS Claude)
+ [x] [Google PaLM2/Gemini 系列模型](https://developers.generativeai.google)
+ [x] [Mistral 系列模型](https://mistral.ai/)
+ + [x] [字节跳动豆包大模型](https://console.volcengine.com/ark/region:ark+cn-beijing/model)
+ [x] [百度文心一言系列模型](https://cloud.baidu.com/doc/WENXINWORKSHOP/index.html)
+ [x] [阿里通义千问系列模型](https://help.aliyun.com/document_detail/2400395.html)
+ [x] [讯飞星火认知大模型](https://www.xfyun.cn/doc/spark/Web.html)
@@ -76,7 +77,6 @@ _✨ 通过标准的 OpenAI API 格式访问所有的大模型,开箱即用
+ [x] [腾讯混元大模型](https://cloud.tencent.com/document/product/1729)
+ [x] [Moonshot AI](https://platform.moonshot.cn/)
+ [x] [百川大模型](https://platform.baichuan-ai.com)
- + [ ] [字节云雀大模型](https://www.volcengine.com/product/ark) (WIP)
+ [x] [MINIMAX](https://api.minimax.chat/)
+ [x] [Groq](https://wow.groq.com/)
+ [x] [Ollama](https://github.com/ollama/ollama)
diff --git a/relay/adaptor/doubao/constants.go b/relay/adaptor/doubao/constants.go
new file mode 100644
index 00000000..dbe819dd
--- /dev/null
+++ b/relay/adaptor/doubao/constants.go
@@ -0,0 +1,13 @@
+package doubao
+
+// https://console.volcengine.com/ark/region:ark+cn-beijing/model
+
+var ModelList = []string{
+ "Doubao-pro-128k",
+ "Doubao-pro-32k",
+ "Doubao-pro-4k",
+ "Doubao-lite-128k",
+ "Doubao-lite-32k",
+ "Doubao-lite-4k",
+ "Doubao-embedding",
+}
diff --git a/relay/adaptor/doubao/main.go b/relay/adaptor/doubao/main.go
new file mode 100644
index 00000000..ea26e6ba
--- /dev/null
+++ b/relay/adaptor/doubao/main.go
@@ -0,0 +1,14 @@
+package doubao
+
+import (
+ "fmt"
+ "github.com/songquanpeng/one-api/relay/meta"
+ "github.com/songquanpeng/one-api/relay/relaymode"
+)
+
+func GetRequestURL(meta *meta.Meta) (string, error) {
+ if meta.Mode == relaymode.ChatCompletions {
+ return fmt.Sprintf("%s/api/v3/chat/completions", meta.BaseURL), nil
+ }
+ return "", fmt.Errorf("unsupported relay mode %d for doubao", meta.Mode)
+}
diff --git a/relay/adaptor/openai/adaptor.go b/relay/adaptor/openai/adaptor.go
index 2e2e4100..db569e4f 100644
--- a/relay/adaptor/openai/adaptor.go
+++ b/relay/adaptor/openai/adaptor.go
@@ -5,6 +5,7 @@ import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/songquanpeng/one-api/relay/adaptor"
+ "github.com/songquanpeng/one-api/relay/adaptor/doubao"
"github.com/songquanpeng/one-api/relay/adaptor/minimax"
"github.com/songquanpeng/one-api/relay/channeltype"
"github.com/songquanpeng/one-api/relay/meta"
@@ -45,6 +46,8 @@ func (a *Adaptor) GetRequestURL(meta *meta.Meta) (string, error) {
return GetFullRequestURL(meta.BaseURL, requestURL, meta.ChannelType), nil
case channeltype.Minimax:
return minimax.GetRequestURL(meta)
+ case channeltype.Doubao:
+ return doubao.GetRequestURL(meta)
default:
return GetFullRequestURL(meta.BaseURL, meta.RequestURLPath, meta.ChannelType), nil
}
diff --git a/relay/adaptor/openai/compatible.go b/relay/adaptor/openai/compatible.go
index 0116a2eb..5d5b4008 100644
--- a/relay/adaptor/openai/compatible.go
+++ b/relay/adaptor/openai/compatible.go
@@ -4,6 +4,7 @@ import (
"github.com/songquanpeng/one-api/relay/adaptor/ai360"
"github.com/songquanpeng/one-api/relay/adaptor/baichuan"
"github.com/songquanpeng/one-api/relay/adaptor/deepseek"
+ "github.com/songquanpeng/one-api/relay/adaptor/doubao"
"github.com/songquanpeng/one-api/relay/adaptor/groq"
"github.com/songquanpeng/one-api/relay/adaptor/lingyiwanwu"
"github.com/songquanpeng/one-api/relay/adaptor/minimax"
@@ -20,6 +21,7 @@ var CompatibleChannels = []int{
channeltype.Moonshot,
channeltype.Baichuan,
channeltype.Minimax,
+ channeltype.Doubao,
channeltype.Mistral,
channeltype.Groq,
channeltype.LingYiWanWu,
@@ -52,6 +54,8 @@ func GetCompatibleChannelMeta(channelType int) (string, []string) {
return "deepseek", deepseek.ModelList
case channeltype.TogetherAI:
return "together.ai", togetherai.ModelList
+ case channeltype.Doubao:
+ return "doubao", doubao.ModelList
default:
return "openai", ModelList
}
diff --git a/relay/channeltype/define.go b/relay/channeltype/define.go
index 60964565..d8885ae9 100644
--- a/relay/channeltype/define.go
+++ b/relay/channeltype/define.go
@@ -41,6 +41,6 @@ const (
Cloudflare
DeepL
TogetherAI
-
+ Doubao
Dummy
)
diff --git a/relay/channeltype/url.go b/relay/channeltype/url.go
index f5767f47..489a21de 100644
--- a/relay/channeltype/url.go
+++ b/relay/channeltype/url.go
@@ -41,6 +41,7 @@ var ChannelBaseURLs = []string{
"https://api.cloudflare.com", // 37
"https://api-free.deepl.com", // 38
"https://api.together.xyz", // 39
+ "https://ark.cn-beijing.volces.com", // 40
}
func init() {
diff --git a/web/berry/src/constants/ChannelConstants.js b/web/berry/src/constants/ChannelConstants.js
index 589ef1fb..52d64083 100644
--- a/web/berry/src/constants/ChannelConstants.js
+++ b/web/berry/src/constants/ChannelConstants.js
@@ -47,6 +47,12 @@ export const CHANNEL_OPTIONS = {
value: 28,
color: 'warning'
},
+ 40: {
+ key: 40,
+ text: '字节跳动豆包',
+ value: 40,
+ color: 'primary'
+ },
15: {
key: 15,
text: '百度文心千帆',
diff --git a/web/default/src/constants/channel.constants.js b/web/default/src/constants/channel.constants.js
index 43ee51b7..f9cf2fc5 100644
--- a/web/default/src/constants/channel.constants.js
+++ b/web/default/src/constants/channel.constants.js
@@ -6,6 +6,7 @@ export const CHANNEL_OPTIONS = [
{key: 11, text: 'Google PaLM2', value: 11, color: 'orange'},
{key: 24, text: 'Google Gemini', value: 24, color: 'orange'},
{key: 28, text: 'Mistral AI', value: 28, color: 'orange'},
+ {key: 40, text: '字节跳动豆包', value: 40, color: 'blue'},
{key: 15, text: '百度文心千帆', value: 15, color: 'blue'},
{key: 17, text: '阿里通义千问', value: 17, color: 'orange'},
{key: 18, text: '讯飞星火认知', value: 18, color: 'blue'},
diff --git a/web/default/src/pages/Channel/EditChannel.js b/web/default/src/pages/Channel/EditChannel.js
index 5c7f13ff..ac0fed60 100644
--- a/web/default/src/pages/Channel/EditChannel.js
+++ b/web/default/src/pages/Channel/EditChannel.js
@@ -362,6 +362,13 @@ const EditChannel = () => {
)
}
+ {
+ inputs.type === 40 && (
+
+ 对于豆包而言,需要手动去 模型推理页面 创建推理接入点,以接入点名称作为模型名称,例如:`ep-20240608051426-tkxvl`。
+
+ )
+ }
Date: Sat, 8 Jun 2024 13:34:23 +0800
Subject: [PATCH 10/19] feat: post all messages for cloudflare (#1515)
---
relay/adaptor/cloudflare/main.go | 20 +++++++++++++-------
1 file changed, 13 insertions(+), 7 deletions(-)
diff --git a/relay/adaptor/cloudflare/main.go b/relay/adaptor/cloudflare/main.go
index e85bbc25..f6d496f7 100644
--- a/relay/adaptor/cloudflare/main.go
+++ b/relay/adaptor/cloudflare/main.go
@@ -17,15 +17,21 @@ import (
)
func ConvertRequest(textRequest model.GeneralOpenAIRequest) *Request {
- lastMessage := textRequest.Messages[len(textRequest.Messages)-1]
- return &Request{
- MaxTokens: textRequest.MaxTokens,
- Prompt: lastMessage.StringContent(),
- Stream: textRequest.Stream,
- Temperature: textRequest.Temperature,
- }
+ var promptBuilder strings.Builder
+ for _, message := range textRequest.Messages {
+ promptBuilder.WriteString(message.StringContent())
+ promptBuilder.WriteString("\n") // 添加换行符来分隔每个消息
+ }
+
+ return &Request{
+ MaxTokens: textRequest.MaxTokens,
+ Prompt: promptBuilder.String(),
+ Stream: textRequest.Stream,
+ Temperature: textRequest.Temperature,
+ }
}
+
func ResponseCloudflare2OpenAI(cloudflareResponse *Response) *openai.TextResponse {
choice := openai.TextResponseChoice{
Index: 0,
From f83894c83f480fbb06150dddf6dca11e1022ae7c Mon Sep 17 00:00:00 2001
From: wagxuebing <65780532+lynnssb@users.noreply.github.com>
Date: Wed, 12 Jun 2024 23:12:58 +0800
Subject: [PATCH 11/19] fix: xunfei interface call 4001 error (#1499)
Co-authored-by: lynnssb
---
relay/adaptor/xunfei/model.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/relay/adaptor/xunfei/model.go b/relay/adaptor/xunfei/model.go
index 97a43154..5ee17cb0 100644
--- a/relay/adaptor/xunfei/model.go
+++ b/relay/adaptor/xunfei/model.go
@@ -26,7 +26,7 @@ type ChatRequest struct {
Message struct {
Text []Message `json:"text"`
} `json:"message"`
- Functions struct {
+ Functions *struct {
Text []model.Function `json:"text,omitempty"`
} `json:"functions,omitempty"`
} `json:"payload"`
From c1971870fa4d2f6c7c406fa644bc5b55737461c4 Mon Sep 17 00:00:00 2001
From: Zhong Liu
Date: Thu, 13 Jun 2024 00:07:26 +0800
Subject: [PATCH 12/19] fix: support for Spark Lite model (#1526)
* fix: Support for Spark Lite model
* fix: fix panic
* fix: fix xunfei version config
---------
Co-authored-by: JustSong <39998050+songquanpeng@users.noreply.github.com>
Co-authored-by: JustSong
---
middleware/distributor.go | 42 ++++++++++----------
model/channel.go | 2 +-
relay/adaptor/xunfei/adaptor.go | 16 ++++----
relay/adaptor/xunfei/main.go | 25 ++++++------
relay/adaptor/xunfei/model.go | 8 ++--
web/default/src/pages/Channel/EditChannel.js | 3 --
6 files changed, 48 insertions(+), 48 deletions(-)
diff --git a/middleware/distributor.go b/middleware/distributor.go
index d0fd7ba5..0c4b04c3 100644
--- a/middleware/distributor.go
+++ b/middleware/distributor.go
@@ -67,26 +67,28 @@ func SetupContextForSelectedChannel(c *gin.Context, channel *model.Channel, mode
c.Set(ctxkey.BaseURL, channel.GetBaseURL())
cfg, _ := channel.LoadConfig()
// this is for backward compatibility
- switch channel.Type {
- case channeltype.Azure:
- if cfg.APIVersion == "" {
- cfg.APIVersion = channel.Other
- }
- case channeltype.Xunfei:
- if cfg.APIVersion == "" {
- cfg.APIVersion = channel.Other
- }
- case channeltype.Gemini:
- if cfg.APIVersion == "" {
- cfg.APIVersion = channel.Other
- }
- case channeltype.AIProxyLibrary:
- if cfg.LibraryID == "" {
- cfg.LibraryID = channel.Other
- }
- case channeltype.Ali:
- if cfg.Plugin == "" {
- cfg.Plugin = channel.Other
+ if channel.Other != nil {
+ switch channel.Type {
+ case channeltype.Azure:
+ if cfg.APIVersion == "" {
+ cfg.APIVersion = *channel.Other
+ }
+ case channeltype.Xunfei:
+ if cfg.APIVersion == "" {
+ cfg.APIVersion = *channel.Other
+ }
+ case channeltype.Gemini:
+ if cfg.APIVersion == "" {
+ cfg.APIVersion = *channel.Other
+ }
+ case channeltype.AIProxyLibrary:
+ if cfg.LibraryID == "" {
+ cfg.LibraryID = *channel.Other
+ }
+ case channeltype.Ali:
+ if cfg.Plugin == "" {
+ cfg.Plugin = *channel.Other
+ }
}
}
c.Set(ctxkey.Config, cfg)
diff --git a/model/channel.go b/model/channel.go
index ec52683e..a92f402c 100644
--- a/model/channel.go
+++ b/model/channel.go
@@ -27,7 +27,7 @@ type Channel struct {
TestTime int64 `json:"test_time" gorm:"bigint"`
ResponseTime int `json:"response_time"` // in milliseconds
BaseURL *string `json:"base_url" gorm:"column:base_url;default:''"`
- Other string `json:"other"` // DEPRECATED: please save config to field Config
+ Other *string `json:"other"` // DEPRECATED: please save config to field Config
Balance float64 `json:"balance"` // in USD
BalanceUpdatedTime int64 `json:"balance_updated_time" gorm:"bigint"`
Models string `json:"models"`
diff --git a/relay/adaptor/xunfei/adaptor.go b/relay/adaptor/xunfei/adaptor.go
index 3af97831..b5967f26 100644
--- a/relay/adaptor/xunfei/adaptor.go
+++ b/relay/adaptor/xunfei/adaptor.go
@@ -27,14 +27,6 @@ func (a *Adaptor) GetRequestURL(meta *meta.Meta) (string, error) {
func (a *Adaptor) SetupRequestHeader(c *gin.Context, req *http.Request, meta *meta.Meta) error {
adaptor.SetupCommonRequestHeader(c, req, meta)
- version := parseAPIVersionByModelName(meta.ActualModelName)
- if version == "" {
- version = a.meta.Config.APIVersion
- }
- if version == "" {
- version = "v1.1"
- }
- a.meta.Config.APIVersion = version
// check DoResponse for auth part
return nil
}
@@ -69,6 +61,14 @@ func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, meta *meta.Met
if a.request == nil {
return nil, openai.ErrorWrapper(errors.New("request is nil"), "request_is_nil", http.StatusBadRequest)
}
+ version := parseAPIVersionByModelName(meta.ActualModelName)
+ if version == "" {
+ version = a.meta.Config.APIVersion
+ }
+ if version == "" {
+ version = "v1.1"
+ }
+ a.meta.Config.APIVersion = version
if meta.IsStream {
err, usage = StreamHandler(c, meta, *a.request, splits[0], splits[1], splits[2])
} else {
diff --git a/relay/adaptor/xunfei/main.go b/relay/adaptor/xunfei/main.go
index c3e768b7..00122d5e 100644
--- a/relay/adaptor/xunfei/main.go
+++ b/relay/adaptor/xunfei/main.go
@@ -5,7 +5,14 @@ import (
"crypto/sha256"
"encoding/base64"
"encoding/json"
+ "errors"
"fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "strings"
+ "time"
+
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"github.com/songquanpeng/one-api/common"
@@ -16,11 +23,6 @@ import (
"github.com/songquanpeng/one-api/relay/constant"
"github.com/songquanpeng/one-api/relay/meta"
"github.com/songquanpeng/one-api/relay/model"
- "io"
- "net/http"
- "net/url"
- "strings"
- "time"
)
// https://console.xfyun.cn/services/cbm
@@ -28,11 +30,7 @@ import (
func requestOpenAI2Xunfei(request model.GeneralOpenAIRequest, xunfeiAppId string, domain string) *ChatRequest {
messages := make([]Message, 0, len(request.Messages))
- var lastToolCalls []model.Tool
for _, message := range request.Messages {
- if message.ToolCalls != nil {
- lastToolCalls = message.ToolCalls
- }
messages = append(messages, Message{
Role: message.Role,
Content: message.StringContent(),
@@ -45,9 +43,10 @@ func requestOpenAI2Xunfei(request model.GeneralOpenAIRequest, xunfeiAppId string
xunfeiRequest.Parameter.Chat.TopK = request.N
xunfeiRequest.Parameter.Chat.MaxTokens = request.MaxTokens
xunfeiRequest.Payload.Message.Text = messages
- if len(lastToolCalls) != 0 {
- for _, toolCall := range lastToolCalls {
- xunfeiRequest.Payload.Functions.Text = append(xunfeiRequest.Payload.Functions.Text, toolCall.Function)
+
+ if strings.HasPrefix(domain, "generalv3") {
+ xunfeiRequest.Payload.Functions = &Functions{
+ Text: request.Tools,
}
}
@@ -203,7 +202,7 @@ func Handler(c *gin.Context, meta *meta.Meta, textRequest model.GeneralOpenAIReq
}
}
if len(xunfeiResponse.Payload.Choices.Text) == 0 {
- return openai.ErrorWrapper(err, "xunfei_empty_response_detected", http.StatusInternalServerError), nil
+ return openai.ErrorWrapper(errors.New("xunfei empty response detected"), "xunfei_empty_response_detected", http.StatusInternalServerError), nil
}
xunfeiResponse.Payload.Choices.Text[0].Content = content
diff --git a/relay/adaptor/xunfei/model.go b/relay/adaptor/xunfei/model.go
index 5ee17cb0..681c0c05 100644
--- a/relay/adaptor/xunfei/model.go
+++ b/relay/adaptor/xunfei/model.go
@@ -9,6 +9,10 @@ type Message struct {
Content string `json:"content"`
}
+type Functions struct {
+ Text []model.Tool `json:"text,omitempty"`
+}
+
type ChatRequest struct {
Header struct {
AppId string `json:"app_id"`
@@ -26,9 +30,7 @@ type ChatRequest struct {
Message struct {
Text []Message `json:"text"`
} `json:"message"`
- Functions *struct {
- Text []model.Function `json:"text,omitempty"`
- } `json:"functions,omitempty"`
+ Functions *Functions `json:"functions,omitempty"`
} `json:"payload"`
}
diff --git a/web/default/src/pages/Channel/EditChannel.js b/web/default/src/pages/Channel/EditChannel.js
index ac0fed60..fb80a726 100644
--- a/web/default/src/pages/Channel/EditChannel.js
+++ b/web/default/src/pages/Channel/EditChannel.js
@@ -181,9 +181,6 @@ const EditChannel = () => {
if (localInputs.type === 3 && localInputs.other === '') {
localInputs.other = '2024-03-01-preview';
}
- if (localInputs.type === 18 && localInputs.other === '') {
- localInputs.other = 'v2.1';
- }
let res;
localInputs.models = localInputs.models.join(',');
localInputs.group = localInputs.groups.join(',');
From 6aafb7a99ed9ccd8905ade480f133e0c9bb138d6 Mon Sep 17 00:00:00 2001
From: Buer <42402987+MartialBE@users.noreply.github.com>
Date: Thu, 13 Jun 2024 00:08:49 +0800
Subject: [PATCH 13/19] fix: channel edit settings key error (#1496)
---
web/berry/src/views/Channel/component/EditModal.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/web/berry/src/views/Channel/component/EditModal.js b/web/berry/src/views/Channel/component/EditModal.js
index 4f7f216d..29bd3de2 100644
--- a/web/berry/src/views/Channel/component/EditModal.js
+++ b/web/berry/src/views/Channel/component/EditModal.js
@@ -163,7 +163,7 @@ const EditModal = ({ open, channelId, onCancel, onOk }) => {
values.other = 'v2.1';
}
if (values.key === '') {
- if (values.config.ak !== '' && values.config.sk !== '' && values.config.region !== '') {
+ if (values.config.ak && values.config.sk && values.config.region) {
values.key = `${values.config.ak}|${values.config.sk}|${values.config.region}`;
}
}
From f74577141c881256c8385963ea28166c42b3bfc0 Mon Sep 17 00:00:00 2001
From: jinjianming <57084209+jinjianming@users.noreply.github.com>
Date: Thu, 13 Jun 2024 00:20:48 +0800
Subject: [PATCH 14/19] fix: fix default token not created in some cases
(#1510)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* 修复git、微信等用户注册不会创建默认令牌问题
修复git、微信等用户注册不会创建默认令牌问题
* 修复git、微信等用户注册不会创建默认令牌问题
删除普通用户注册代码
* fix: do not block if error happened
---------
Co-authored-by: JustSong
---
controller/user.go | 26 +-------------------------
model/user.go | 17 +++++++++++++++++
2 files changed, 18 insertions(+), 25 deletions(-)
diff --git a/controller/user.go b/controller/user.go
index 9ab37b5a..e79881c2 100644
--- a/controller/user.go
+++ b/controller/user.go
@@ -6,8 +6,6 @@ import (
"github.com/songquanpeng/one-api/common"
"github.com/songquanpeng/one-api/common/config"
"github.com/songquanpeng/one-api/common/ctxkey"
- "github.com/songquanpeng/one-api/common/helper"
- "github.com/songquanpeng/one-api/common/logger"
"github.com/songquanpeng/one-api/common/random"
"github.com/songquanpeng/one-api/model"
"net/http"
@@ -111,7 +109,6 @@ func Logout(c *gin.Context) {
}
func Register(c *gin.Context) {
- ctx := c.Request.Context()
if !config.RegisterEnabled {
c.JSON(http.StatusOK, gin.H{
"message": "管理员关闭了新用户注册",
@@ -176,28 +173,7 @@ func Register(c *gin.Context) {
})
return
}
- go func() {
- err := user.ValidateAndFill()
- if err != nil {
- logger.Errorf(ctx, "user.ValidateAndFill failed: %w", err)
- return
- }
- cleanToken := model.Token{
- UserId: user.Id,
- Name: "default",
- Key: random.GenerateKey(),
- CreatedTime: helper.GetTimestamp(),
- AccessedTime: helper.GetTimestamp(),
- ExpiredTime: -1,
- RemainQuota: -1,
- UnlimitedQuota: true,
- }
- err = cleanToken.Insert()
- if err != nil {
- logger.Errorf(ctx, "cleanToken.Insert failed: %w", err)
- return
- }
- }()
+
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "",
diff --git a/model/user.go b/model/user.go
index 1dc633b1..924d72f9 100644
--- a/model/user.go
+++ b/model/user.go
@@ -6,6 +6,7 @@ import (
"github.com/songquanpeng/one-api/common"
"github.com/songquanpeng/one-api/common/blacklist"
"github.com/songquanpeng/one-api/common/config"
+ "github.com/songquanpeng/one-api/common/helper"
"github.com/songquanpeng/one-api/common/logger"
"github.com/songquanpeng/one-api/common/random"
"gorm.io/gorm"
@@ -140,6 +141,22 @@ func (user *User) Insert(inviterId int) error {
RecordLog(inviterId, LogTypeSystem, fmt.Sprintf("邀请用户赠送 %s", common.LogQuota(config.QuotaForInviter)))
}
}
+ // create default token
+ cleanToken := Token{
+ UserId: user.Id,
+ Name: "default",
+ Key: random.GenerateKey(),
+ CreatedTime: helper.GetTimestamp(),
+ AccessedTime: helper.GetTimestamp(),
+ ExpiredTime: -1,
+ RemainQuota: -1,
+ UnlimitedQuota: true,
+ }
+ result.Error = cleanToken.Insert()
+ if result.Error != nil {
+ // do not block
+ logger.SysError(fmt.Sprintf("create default token for user %d failed: %s", user.Id, result.Error.Error()))
+ }
return nil
}
From 6ccf3f3cfcfcfcaf1b63ca0f546d1a885523c188 Mon Sep 17 00:00:00 2001
From: JustSong
Date: Thu, 13 Jun 2024 00:28:56 +0800
Subject: [PATCH 15/19] chore: add logger.SysLogf function
---
common/logger/logger.go | 8 ++++++++
main.go | 2 +-
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/common/logger/logger.go b/common/logger/logger.go
index c3dcd89d..f725c619 100644
--- a/common/logger/logger.go
+++ b/common/logger/logger.go
@@ -43,11 +43,19 @@ func SysLog(s string) {
_, _ = fmt.Fprintf(gin.DefaultWriter, "[SYS] %v | %s \n", t.Format("2006/01/02 - 15:04:05"), s)
}
+func SysLogf(format string, a ...any) {
+ SysLog(fmt.Sprintf(format, a...))
+}
+
func SysError(s string) {
t := time.Now()
_, _ = fmt.Fprintf(gin.DefaultErrorWriter, "[SYS] %v | %s \n", t.Format("2006/01/02 - 15:04:05"), s)
}
+func SysErrorf(format string, a ...any) {
+ SysError(fmt.Sprintf(format, a...))
+}
+
func Debug(ctx context.Context, msg string) {
if config.DebugEnabled {
logHelper(ctx, loggerDEBUG, msg)
diff --git a/main.go b/main.go
index eb6f368c..95f8f62b 100644
--- a/main.go
+++ b/main.go
@@ -24,7 +24,7 @@ var buildFS embed.FS
func main() {
logger.SetupLogger()
- logger.SysLog(fmt.Sprintf("One API %s started", common.Version))
+ logger.SysLogf("One API %s started", common.Version)
if os.Getenv("GIN_MODE") != "debug" {
gin.SetMode(gin.ReleaseMode)
}
From ed717211aaa1b1fa968aed493b80402de204df39 Mon Sep 17 00:00:00 2001
From: JustSong
Date: Thu, 13 Jun 2024 00:35:37 +0800
Subject: [PATCH 16/19] chore: adjust default rate limit config
---
common/config/config.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/common/config/config.go b/common/config/config.go
index 539eeef4..4f1c25b6 100644
--- a/common/config/config.go
+++ b/common/config/config.go
@@ -117,10 +117,10 @@ var ValidThemes = map[string]bool{
// All duration's unit is seconds
// Shouldn't larger then RateLimitKeyExpirationDuration
var (
- GlobalApiRateLimitNum = env.Int("GLOBAL_API_RATE_LIMIT", 180)
+ GlobalApiRateLimitNum = env.Int("GLOBAL_API_RATE_LIMIT", 240)
GlobalApiRateLimitDuration int64 = 3 * 60
- GlobalWebRateLimitNum = env.Int("GLOBAL_WEB_RATE_LIMIT", 60)
+ GlobalWebRateLimitNum = env.Int("GLOBAL_WEB_RATE_LIMIT", 120)
GlobalWebRateLimitDuration int64 = 3 * 60
UploadRateLimitNum = 10
From b1520b308b1b573c0b9d2397579a4cd067509c93 Mon Sep 17 00:00:00 2001
From: Wei Tingjiang
Date: Fri, 14 Jun 2024 00:30:47 +0800
Subject: [PATCH 17/19] Try to fix Gemini streaming return being truncated by
FinishReason. (#1477)
1
---
relay/adaptor/gemini/main.go | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/relay/adaptor/gemini/main.go b/relay/adaptor/gemini/main.go
index 534b2708..74a7d5d5 100644
--- a/relay/adaptor/gemini/main.go
+++ b/relay/adaptor/gemini/main.go
@@ -245,8 +245,10 @@ func responseGeminiChat2OpenAI(response *ChatResponse) *openai.TextResponse {
func streamResponseGeminiChat2OpenAI(geminiResponse *ChatResponse) *openai.ChatCompletionsStreamResponse {
var choice openai.ChatCompletionsStreamResponseChoice
choice.Delta.Content = geminiResponse.GetResponseText()
- choice.FinishReason = &constant.StopFinishReason
+ //choice.FinishReason = &constant.StopFinishReason
var response openai.ChatCompletionsStreamResponse
+ response.Id = fmt.Sprintf("chatcmpl-%s", random.GetUUID())
+ response.Created = helper.GetTimestamp()
response.Object = "chat.completion.chunk"
response.Model = "gemini"
response.Choices = []openai.ChatCompletionsStreamResponseChoice{choice}
From 279caf82dc26bcc9d45ab3712e1b89d4f7f666af Mon Sep 17 00:00:00 2001
From: lihangfu <280001404@qq.com>
Date: Thu, 20 Jun 2024 00:23:08 +0800
Subject: [PATCH 18/19] feat: support tencent v3 api (#1542)
Co-authored-by: lihangfu
---
relay/adaptor/tencent/adaptor.go | 24 +++---
relay/adaptor/tencent/constants.go | 7 +-
relay/adaptor/tencent/main.go | 119 ++++++++++++++++++-----------
relay/adaptor/tencent/model.go | 100 +++++++++++++-----------
relay/channeltype/url.go | 2 +-
5 files changed, 151 insertions(+), 101 deletions(-)
diff --git a/relay/adaptor/tencent/adaptor.go b/relay/adaptor/tencent/adaptor.go
index a97476d6..0de92d4a 100644
--- a/relay/adaptor/tencent/adaptor.go
+++ b/relay/adaptor/tencent/adaptor.go
@@ -2,35 +2,43 @@ package tencent
import (
"errors"
- "fmt"
"github.com/gin-gonic/gin"
+ "github.com/songquanpeng/one-api/common/helper"
"github.com/songquanpeng/one-api/relay/adaptor"
"github.com/songquanpeng/one-api/relay/adaptor/openai"
"github.com/songquanpeng/one-api/relay/meta"
"github.com/songquanpeng/one-api/relay/model"
"io"
"net/http"
+ "strconv"
"strings"
)
// https://cloud.tencent.com/document/api/1729/101837
type Adaptor struct {
- Sign string
+ Sign string
+ Action string
+ Version string
+ Timestamp int64
}
func (a *Adaptor) Init(meta *meta.Meta) {
-
+ a.Action = "ChatCompletions"
+ a.Version = "2023-09-01"
+ a.Timestamp = helper.GetTimestamp()
}
func (a *Adaptor) GetRequestURL(meta *meta.Meta) (string, error) {
- return fmt.Sprintf("%s/hyllm/v1/chat/completions", meta.BaseURL), nil
+ return meta.BaseURL + "/", nil
}
func (a *Adaptor) SetupRequestHeader(c *gin.Context, req *http.Request, meta *meta.Meta) error {
adaptor.SetupCommonRequestHeader(c, req, meta)
req.Header.Set("Authorization", a.Sign)
- req.Header.Set("X-TC-Action", meta.ActualModelName)
+ req.Header.Set("X-TC-Action", a.Action)
+ req.Header.Set("X-TC-Version", a.Version)
+ req.Header.Set("X-TC-Timestamp", strconv.FormatInt(a.Timestamp, 10))
return nil
}
@@ -40,15 +48,13 @@ func (a *Adaptor) ConvertRequest(c *gin.Context, relayMode int, request *model.G
}
apiKey := c.Request.Header.Get("Authorization")
apiKey = strings.TrimPrefix(apiKey, "Bearer ")
- appId, secretId, secretKey, err := ParseConfig(apiKey)
+ _, secretId, secretKey, err := ParseConfig(apiKey)
if err != nil {
return nil, err
}
tencentRequest := ConvertRequest(*request)
- tencentRequest.AppId = appId
- tencentRequest.SecretId = secretId
// we have to calculate the sign here
- a.Sign = GetSign(*tencentRequest, secretKey)
+ a.Sign = GetSign(*tencentRequest, a, secretId, secretKey)
return tencentRequest, nil
}
diff --git a/relay/adaptor/tencent/constants.go b/relay/adaptor/tencent/constants.go
index fe176c2c..be415a94 100644
--- a/relay/adaptor/tencent/constants.go
+++ b/relay/adaptor/tencent/constants.go
@@ -1,7 +1,8 @@
package tencent
var ModelList = []string{
- "ChatPro",
- "ChatStd",
- "hunyuan",
+ "hunyuan-lite",
+ "hunyuan-standard",
+ "hunyuan-standard-256K",
+ "hunyuan-pro",
}
diff --git a/relay/adaptor/tencent/main.go b/relay/adaptor/tencent/main.go
index 2ca5724e..0a57dcf7 100644
--- a/relay/adaptor/tencent/main.go
+++ b/relay/adaptor/tencent/main.go
@@ -3,8 +3,8 @@ package tencent
import (
"bufio"
"crypto/hmac"
- "crypto/sha1"
- "encoding/base64"
+ "crypto/sha256"
+ "encoding/hex"
"encoding/json"
"errors"
"fmt"
@@ -19,34 +19,26 @@ import (
"github.com/songquanpeng/one-api/relay/model"
"io"
"net/http"
- "sort"
"strconv"
"strings"
+ "time"
)
-// https://cloud.tencent.com/document/product/1729/97732
-
func ConvertRequest(request model.GeneralOpenAIRequest) *ChatRequest {
- messages := make([]Message, 0, len(request.Messages))
+ messages := make([]*Message, 0, len(request.Messages))
for i := 0; i < len(request.Messages); i++ {
message := request.Messages[i]
- messages = append(messages, Message{
+ messages = append(messages, &Message{
Content: message.StringContent(),
Role: message.Role,
})
}
- stream := 0
- if request.Stream {
- stream = 1
- }
return &ChatRequest{
- Timestamp: helper.GetTimestamp(),
- Expired: helper.GetTimestamp() + 24*60*60,
- QueryID: random.GetUUID(),
- Temperature: request.Temperature,
- TopP: request.TopP,
- Stream: stream,
+ Model: &request.Model,
+ Stream: &request.Stream,
Messages: messages,
+ TopP: &request.TopP,
+ Temperature: &request.Temperature,
}
}
@@ -54,7 +46,11 @@ func responseTencent2OpenAI(response *ChatResponse) *openai.TextResponse {
fullTextResponse := openai.TextResponse{
Object: "chat.completion",
Created: helper.GetTimestamp(),
- Usage: response.Usage,
+ Usage: model.Usage{
+ PromptTokens: response.Usage.PromptTokens,
+ CompletionTokens: response.Usage.CompletionTokens,
+ TotalTokens: response.Usage.TotalTokens,
+ },
}
if len(response.Choices) > 0 {
choice := openai.TextResponseChoice{
@@ -154,6 +150,7 @@ func StreamHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusC
func Handler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusCode, *model.Usage) {
var TencentResponse ChatResponse
+ var responseP ChatResponseP
responseBody, err := io.ReadAll(resp.Body)
if err != nil {
return openai.ErrorWrapper(err, "read_response_body_failed", http.StatusInternalServerError), nil
@@ -162,10 +159,11 @@ func Handler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusCode, *
if err != nil {
return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil
}
- err = json.Unmarshal(responseBody, &TencentResponse)
+ err = json.Unmarshal(responseBody, &responseP)
if err != nil {
return openai.ErrorWrapper(err, "unmarshal_response_body_failed", http.StatusInternalServerError), nil
}
+ TencentResponse = responseP.Response
if TencentResponse.Error.Code != 0 {
return &model.ErrorWithStatusCode{
Error: model.Error{
@@ -202,29 +200,62 @@ func ParseConfig(config string) (appId int64, secretId string, secretKey string,
return
}
-func GetSign(req ChatRequest, secretKey string) string {
- params := make([]string, 0)
- params = append(params, "app_id="+strconv.FormatInt(req.AppId, 10))
- params = append(params, "secret_id="+req.SecretId)
- params = append(params, "timestamp="+strconv.FormatInt(req.Timestamp, 10))
- params = append(params, "query_id="+req.QueryID)
- params = append(params, "temperature="+strconv.FormatFloat(req.Temperature, 'f', -1, 64))
- params = append(params, "top_p="+strconv.FormatFloat(req.TopP, 'f', -1, 64))
- params = append(params, "stream="+strconv.Itoa(req.Stream))
- params = append(params, "expired="+strconv.FormatInt(req.Expired, 10))
-
- var messageStr string
- for _, msg := range req.Messages {
- messageStr += fmt.Sprintf(`{"role":"%s","content":"%s"},`, msg.Role, msg.Content)
- }
- messageStr = strings.TrimSuffix(messageStr, ",")
- params = append(params, "messages=["+messageStr+"]")
-
- sort.Strings(params)
- url := "hunyuan.cloud.tencent.com/hyllm/v1/chat/completions?" + strings.Join(params, "&")
- mac := hmac.New(sha1.New, []byte(secretKey))
- signURL := url
- mac.Write([]byte(signURL))
- sign := mac.Sum([]byte(nil))
- return base64.StdEncoding.EncodeToString(sign)
+func sha256hex(s string) string {
+ b := sha256.Sum256([]byte(s))
+ return hex.EncodeToString(b[:])
+}
+
+func hmacSha256(s, key string) string {
+ hashed := hmac.New(sha256.New, []byte(key))
+ hashed.Write([]byte(s))
+ return string(hashed.Sum(nil))
+}
+
+func GetSign(req ChatRequest, adaptor *Adaptor, secId, secKey string) string {
+ // build canonical request string
+ host := "hunyuan.tencentcloudapi.com"
+ httpRequestMethod := "POST"
+ canonicalURI := "/"
+ canonicalQueryString := ""
+ canonicalHeaders := fmt.Sprintf("content-type:%s\nhost:%s\nx-tc-action:%s\n",
+ "application/json", host, strings.ToLower(adaptor.Action))
+ signedHeaders := "content-type;host;x-tc-action"
+ payload, _ := json.Marshal(req)
+ hashedRequestPayload := sha256hex(string(payload))
+ canonicalRequest := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s",
+ httpRequestMethod,
+ canonicalURI,
+ canonicalQueryString,
+ canonicalHeaders,
+ signedHeaders,
+ hashedRequestPayload)
+ // build string to sign
+ algorithm := "TC3-HMAC-SHA256"
+ requestTimestamp := strconv.FormatInt(adaptor.Timestamp, 10)
+ timestamp, _ := strconv.ParseInt(requestTimestamp, 10, 64)
+ t := time.Unix(timestamp, 0).UTC()
+ // must be the format 2006-01-02, ref to package time for more info
+ date := t.Format("2006-01-02")
+ credentialScope := fmt.Sprintf("%s/%s/tc3_request", date, "hunyuan")
+ hashedCanonicalRequest := sha256hex(canonicalRequest)
+ string2sign := fmt.Sprintf("%s\n%s\n%s\n%s",
+ algorithm,
+ requestTimestamp,
+ credentialScope,
+ hashedCanonicalRequest)
+
+ // sign string
+ secretDate := hmacSha256(date, "TC3"+secKey)
+ secretService := hmacSha256("hunyuan", secretDate)
+ secretKey := hmacSha256("tc3_request", secretService)
+ signature := hex.EncodeToString([]byte(hmacSha256(string2sign, secretKey)))
+
+ // build authorization
+ authorization := fmt.Sprintf("%s Credential=%s/%s, SignedHeaders=%s, Signature=%s",
+ algorithm,
+ secId,
+ credentialScope,
+ signedHeaders,
+ signature)
+ return authorization
}
diff --git a/relay/adaptor/tencent/model.go b/relay/adaptor/tencent/model.go
index 71286be9..fb97724e 100644
--- a/relay/adaptor/tencent/model.go
+++ b/relay/adaptor/tencent/model.go
@@ -1,63 +1,75 @@
package tencent
-import (
- "github.com/songquanpeng/one-api/relay/model"
-)
-
type Message struct {
- Role string `json:"role"`
- Content string `json:"content"`
+ Role string `json:"Role"`
+ Content string `json:"Content"`
}
type ChatRequest struct {
- AppId int64 `json:"app_id"` // 腾讯云账号的 APPID
- SecretId string `json:"secret_id"` // 官网 SecretId
- // Timestamp当前 UNIX 时间戳,单位为秒,可记录发起 API 请求的时间。
- // 例如1529223702,如果与当前时间相差过大,会引起签名过期错误
- Timestamp int64 `json:"timestamp"`
- // Expired 签名的有效期,是一个符合 UNIX Epoch 时间戳规范的数值,
- // 单位为秒;Expired 必须大于 Timestamp 且 Expired-Timestamp 小于90天
- Expired int64 `json:"expired"`
- QueryID string `json:"query_id"` //请求 Id,用于问题排查
- // Temperature 较高的数值会使输出更加随机,而较低的数值会使其更加集中和确定
- // 默认 1.0,取值区间为[0.0,2.0],非必要不建议使用,不合理的取值会影响效果
- // 建议该参数和 top_p 只设置1个,不要同时更改 top_p
- Temperature float64 `json:"temperature"`
- // TopP 影响输出文本的多样性,取值越大,生成文本的多样性越强
- // 默认1.0,取值区间为[0.0, 1.0],非必要不建议使用, 不合理的取值会影响效果
- // 建议该参数和 temperature 只设置1个,不要同时更改
- TopP float64 `json:"top_p"`
- // Stream 0:同步,1:流式 (默认,协议:SSE)
- // 同步请求超时:60s,如果内容较长建议使用流式
- Stream int `json:"stream"`
- // Messages 会话内容, 长度最多为40, 按对话时间从旧到新在数组中排列
- // 输入 content 总数最大支持 3000 token。
- Messages []Message `json:"messages"`
+ // 模型名称,可选值包括 hunyuan-lite、hunyuan-standard、hunyuan-standard-256K、hunyuan-pro。
+ // 各模型介绍请阅读 [产品概述](https://cloud.tencent.com/document/product/1729/104753) 中的说明。
+ //
+ // 注意:
+ // 不同的模型计费不同,请根据 [购买指南](https://cloud.tencent.com/document/product/1729/97731) 按需调用。
+ Model *string `json:"Model"`
+ // 聊天上下文信息。
+ // 说明:
+ // 1. 长度最多为 40,按对话时间从旧到新在数组中排列。
+ // 2. Message.Role 可选值:system、user、assistant。
+ // 其中,system 角色可选,如存在则必须位于列表的最开始。user 和 assistant 需交替出现(一问一答),以 user 提问开始和结束,且 Content 不能为空。Role 的顺序示例:[system(可选) user assistant user assistant user ...]。
+ // 3. Messages 中 Content 总长度不能超过模型输入长度上限(可参考 [产品概述](https://cloud.tencent.com/document/product/1729/104753) 文档),超过则会截断最前面的内容,只保留尾部内容。
+ Messages []*Message `json:"Messages"`
+ // 流式调用开关。
+ // 说明:
+ // 1. 未传值时默认为非流式调用(false)。
+ // 2. 流式调用时以 SSE 协议增量返回结果(返回值取 Choices[n].Delta 中的值,需要拼接增量数据才能获得完整结果)。
+ // 3. 非流式调用时:
+ // 调用方式与普通 HTTP 请求无异。
+ // 接口响应耗时较长,**如需更低时延建议设置为 true**。
+ // 只返回一次最终结果(返回值取 Choices[n].Message 中的值)。
+ //
+ // 注意:
+ // 通过 SDK 调用时,流式和非流式调用需用**不同的方式**获取返回值,具体参考 SDK 中的注释或示例(在各语言 SDK 代码仓库的 examples/hunyuan/v20230901/ 目录中)。
+ Stream *bool `json:"Stream"`
+ // 说明:
+ // 1. 影响输出文本的多样性,取值越大,生成文本的多样性越强。
+ // 2. 取值区间为 [0.0, 1.0],未传值时使用各模型推荐值。
+ // 3. 非必要不建议使用,不合理的取值会影响效果。
+ TopP *float64 `json:"TopP"`
+ // 说明:
+ // 1. 较高的数值会使输出更加随机,而较低的数值会使其更加集中和确定。
+ // 2. 取值区间为 [0.0, 2.0],未传值时使用各模型推荐值。
+ // 3. 非必要不建议使用,不合理的取值会影响效果。
+ Temperature *float64 `json:"Temperature"`
}
type Error struct {
- Code int `json:"code"`
- Message string `json:"message"`
+ Code int `json:"Code"`
+ Message string `json:"Message"`
}
type Usage struct {
- InputTokens int `json:"input_tokens"`
- OutputTokens int `json:"output_tokens"`
- TotalTokens int `json:"total_tokens"`
+ PromptTokens int `json:"PromptTokens"`
+ CompletionTokens int `json:"CompletionTokens"`
+ TotalTokens int `json:"TotalTokens"`
}
type ResponseChoices struct {
- FinishReason string `json:"finish_reason,omitempty"` // 流式结束标志位,为 stop 则表示尾包
- Messages Message `json:"messages,omitempty"` // 内容,同步模式返回内容,流模式为 null 输出 content 内容总数最多支持 1024token。
- Delta Message `json:"delta,omitempty"` // 内容,流模式返回内容,同步模式为 null 输出 content 内容总数最多支持 1024token。
+ FinishReason string `json:"FinishReason,omitempty"` // 流式结束标志位,为 stop 则表示尾包
+ Messages Message `json:"Message,omitempty"` // 内容,同步模式返回内容,流模式为 null 输出 content 内容总数最多支持 1024token。
+ Delta Message `json:"Delta,omitempty"` // 内容,流模式返回内容,同步模式为 null 输出 content 内容总数最多支持 1024token。
}
type ChatResponse struct {
- Choices []ResponseChoices `json:"choices,omitempty"` // 结果
- Created string `json:"created,omitempty"` // unix 时间戳的字符串
- Id string `json:"id,omitempty"` // 会话 id
- Usage model.Usage `json:"usage,omitempty"` // token 数量
- Error Error `json:"error,omitempty"` // 错误信息 注意:此字段可能返回 null,表示取不到有效值
- Note string `json:"note,omitempty"` // 注释
- ReqID string `json:"req_id,omitempty"` // 唯一请求 Id,每次请求都会返回。用于反馈接口入参
+ Choices []ResponseChoices `json:"Choices,omitempty"` // 结果
+ Created int64 `json:"Created,omitempty"` // unix 时间戳的字符串
+ Id string `json:"Id,omitempty"` // 会话 id
+ Usage Usage `json:"Usage,omitempty"` // token 数量
+ Error Error `json:"Error,omitempty"` // 错误信息 注意:此字段可能返回 null,表示取不到有效值
+ Note string `json:"Note,omitempty"` // 注释
+ ReqID string `json:"Req_id,omitempty"` // 唯一请求 Id,每次请求都会返回。用于反馈接口入参
+}
+
+type ChatResponseP struct {
+ Response ChatResponse `json:"Response,omitempty"`
}
diff --git a/relay/channeltype/url.go b/relay/channeltype/url.go
index 489a21de..513d183b 100644
--- a/relay/channeltype/url.go
+++ b/relay/channeltype/url.go
@@ -24,7 +24,7 @@ var ChannelBaseURLs = []string{
"https://openrouter.ai/api", // 20
"https://api.aiproxy.io", // 21
"https://fastgpt.run/api/openapi", // 22
- "https://hunyuan.cloud.tencent.com", // 23
+ "https://hunyuan.tencentcloudapi.com", // 23
"https://generativelanguage.googleapis.com", // 24
"https://api.moonshot.cn", // 25
"https://api.baichuan-ai.com", // 26
From ff196b75a73638c22da54b6cbca856bdaf57ccb2 Mon Sep 17 00:00:00 2001
From: Shi Jilin <40982122+shijilin0116@users.noreply.github.com>
Date: Thu, 20 Jun 2024 22:56:59 +0800
Subject: [PATCH 19/19] fix: fix sparkdesk function call
---
relay/adaptor/xunfei/main.go | 6 +++++-
relay/adaptor/xunfei/model.go | 2 +-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/relay/adaptor/xunfei/main.go b/relay/adaptor/xunfei/main.go
index 00122d5e..39b76e27 100644
--- a/relay/adaptor/xunfei/main.go
+++ b/relay/adaptor/xunfei/main.go
@@ -45,8 +45,12 @@ func requestOpenAI2Xunfei(request model.GeneralOpenAIRequest, xunfeiAppId string
xunfeiRequest.Payload.Message.Text = messages
if strings.HasPrefix(domain, "generalv3") {
+ functions := make([]model.Function, len(request.Tools))
+ for i, tool := range request.Tools {
+ functions[i] = tool.Function
+ }
xunfeiRequest.Payload.Functions = &Functions{
- Text: request.Tools,
+ Text: functions,
}
}
diff --git a/relay/adaptor/xunfei/model.go b/relay/adaptor/xunfei/model.go
index 681c0c05..1f37c046 100644
--- a/relay/adaptor/xunfei/model.go
+++ b/relay/adaptor/xunfei/model.go
@@ -10,7 +10,7 @@ type Message struct {
}
type Functions struct {
- Text []model.Tool `json:"text,omitempty"`
+ Text []model.Function `json:"text,omitempty"`
}
type ChatRequest struct {