Merge branch 'songquanpeng:main' into main
This commit is contained in:
commit
17f2a6ac1c
@ -87,7 +87,7 @@ _✨ 通过标准的 OpenAI API 格式访问所有的大模型,开箱即用
|
|||||||
5. 支持**多机部署**,[详见此处](#多机部署)。
|
5. 支持**多机部署**,[详见此处](#多机部署)。
|
||||||
6. 支持**令牌管理**,设置令牌的过期时间和额度。
|
6. 支持**令牌管理**,设置令牌的过期时间和额度。
|
||||||
7. 支持**兑换码管理**,支持批量生成和导出兑换码,可使用兑换码为账户进行充值。
|
7. 支持**兑换码管理**,支持批量生成和导出兑换码,可使用兑换码为账户进行充值。
|
||||||
8. 支持**通道管理**,批量创建通道。
|
8. 支持**渠道管理**,批量创建渠道。
|
||||||
9. 支持**用户分组**以及**渠道分组**,支持为不同分组设置不同的倍率。
|
9. 支持**用户分组**以及**渠道分组**,支持为不同分组设置不同的倍率。
|
||||||
10. 支持渠道**设置模型列表**。
|
10. 支持渠道**设置模型列表**。
|
||||||
11. 支持**查看额度明细**。
|
11. 支持**查看额度明细**。
|
||||||
@ -421,7 +421,7 @@ https://openai.justsong.cn
|
|||||||
+ 检查你的接口地址和 API Key 有没有填对。
|
+ 检查你的接口地址和 API Key 有没有填对。
|
||||||
+ 检查是否启用了 HTTPS,浏览器会拦截 HTTPS 域名下的 HTTP 请求。
|
+ 检查是否启用了 HTTPS,浏览器会拦截 HTTPS 域名下的 HTTP 请求。
|
||||||
6. 报错:`当前分组负载已饱和,请稍后再试`
|
6. 报错:`当前分组负载已饱和,请稍后再试`
|
||||||
+ 上游通道 429 了。
|
+ 上游渠道 429 了。
|
||||||
7. 升级之后我的数据会丢失吗?
|
7. 升级之后我的数据会丢失吗?
|
||||||
+ 如果使用 MySQL,不会。
|
+ 如果使用 MySQL,不会。
|
||||||
+ 如果使用 SQLite,需要按照我所给的部署命令挂载 volume 持久化 one-api.db 数据库文件,否则容器重启后数据会丢失。
|
+ 如果使用 SQLite,需要按照我所给的部署命令挂载 volume 持久化 one-api.db 数据库文件,否则容器重启后数据会丢失。
|
||||||
@ -429,8 +429,8 @@ https://openai.justsong.cn
|
|||||||
+ 一般情况下不需要,系统将在初始化的时候自动调整。
|
+ 一般情况下不需要,系统将在初始化的时候自动调整。
|
||||||
+ 如果需要的话,我会在更新日志中说明,并给出脚本。
|
+ 如果需要的话,我会在更新日志中说明,并给出脚本。
|
||||||
9. 手动修改数据库后报错:`数据库一致性已被破坏,请联系管理员`?
|
9. 手动修改数据库后报错:`数据库一致性已被破坏,请联系管理员`?
|
||||||
+ 这是检测到 ability 表里有些记录的通道 id 是不存在的,这大概率是因为你删了 channel 表里的记录但是没有同步在 ability 表里清理无效的通道。
|
+ 这是检测到 ability 表里有些记录的渠道 id 是不存在的,这大概率是因为你删了 channel 表里的记录但是没有同步在 ability 表里清理无效的渠道。
|
||||||
+ 对于每一个通道,其所支持的模型都需要有一个专门的 ability 表的记录,表示该通道支持该模型。
|
+ 对于每一个渠道,其所支持的模型都需要有一个专门的 ability 表的记录,表示该渠道支持该模型。
|
||||||
|
|
||||||
## 相关项目
|
## 相关项目
|
||||||
* [FastGPT](https://github.com/labring/FastGPT): 基于 LLM 大语言模型的知识库问答系统
|
* [FastGPT](https://github.com/labring/FastGPT): 基于 LLM 大语言模型的知识库问答系统
|
||||||
|
@ -81,9 +81,12 @@ var ModelRatio = map[string]float64{
|
|||||||
"bge-large-en": 0.002 * RMB,
|
"bge-large-en": 0.002 * RMB,
|
||||||
"bge-large-8k": 0.002 * RMB,
|
"bge-large-8k": 0.002 * RMB,
|
||||||
// https://ai.google.dev/pricing
|
// https://ai.google.dev/pricing
|
||||||
"PaLM-2": 1,
|
"PaLM-2": 1,
|
||||||
"gemini-pro": 1, // $0.00025 / 1k characters -> $0.001 / 1k tokens
|
"gemini-pro": 1, // $0.00025 / 1k characters -> $0.001 / 1k tokens
|
||||||
"gemini-pro-vision": 1, // $0.00025 / 1k characters -> $0.001 / 1k tokens
|
"gemini-pro-vision": 1, // $0.00025 / 1k characters -> $0.001 / 1k tokens
|
||||||
|
"gemini-1.0-pro-vision-001": 1,
|
||||||
|
"gemini-1.0-pro-001": 1,
|
||||||
|
"gemini-1.5-pro": 1,
|
||||||
// https://open.bigmodel.cn/pricing
|
// https://open.bigmodel.cn/pricing
|
||||||
"glm-4": 0.1 * RMB,
|
"glm-4": 0.1 * RMB,
|
||||||
"glm-4v": 0.1 * RMB,
|
"glm-4v": 0.1 * RMB,
|
||||||
@ -251,6 +254,9 @@ func GetCompletionRatio(name string) float64 {
|
|||||||
if strings.HasPrefix(name, "mistral-") {
|
if strings.HasPrefix(name, "mistral-") {
|
||||||
return 3
|
return 3
|
||||||
}
|
}
|
||||||
|
if strings.HasPrefix(name, "gemini-") {
|
||||||
|
return 3
|
||||||
|
}
|
||||||
switch name {
|
switch name {
|
||||||
case "llama2-70b-4096":
|
case "llama2-70b-4096":
|
||||||
return 0.8 / 0.7
|
return 0.8 / 0.7
|
||||||
|
@ -197,7 +197,7 @@ func testChannels(notify bool, scope string) error {
|
|||||||
testAllChannelsRunning = false
|
testAllChannelsRunning = false
|
||||||
testAllChannelsLock.Unlock()
|
testAllChannelsLock.Unlock()
|
||||||
if notify {
|
if notify {
|
||||||
err := message.Notify(message.ByAll, "通道测试完成", "", "通道测试完成,如果没有收到禁用通知,说明所有通道都正常")
|
err := message.Notify(message.ByAll, "渠道测试完成", "", "渠道测试完成,如果没有收到禁用通知,说明所有渠道都正常")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.SysError(fmt.Sprintf("failed to send email: %s", err.Error()))
|
logger.SysError(fmt.Sprintf("failed to send email: %s", err.Error()))
|
||||||
}
|
}
|
||||||
|
@ -142,6 +142,7 @@ func AddToken(c *gin.Context) {
|
|||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"success": true,
|
"success": true,
|
||||||
"message": "",
|
"message": "",
|
||||||
|
"data": cleanToken,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
34
i18n/en.json
34
i18n/en.json
@ -8,12 +8,12 @@
|
|||||||
"确认删除": "Confirm Delete",
|
"确认删除": "Confirm Delete",
|
||||||
"确认绑定": "Confirm Binding",
|
"确认绑定": "Confirm Binding",
|
||||||
"您正在删除自己的帐户,将清空所有数据且不可恢复": "You are deleting your account, all data will be cleared and unrecoverable.",
|
"您正在删除自己的帐户,将清空所有数据且不可恢复": "You are deleting your account, all data will be cleared and unrecoverable.",
|
||||||
"\"通道「%s」(#%d)已被禁用\"": "\"Channel %s (#%d) has been disabled\"",
|
"\"渠道「%s」(#%d)已被禁用\"": "\"Channel %s (#%d) has been disabled\"",
|
||||||
"通道「%s」(#%d)已被禁用,原因:%s": "Channel %s (#%d) has been disabled, reason: %s",
|
"渠道「%s」(#%d)已被禁用,原因:%s": "Channel %s (#%d) has been disabled, reason: %s",
|
||||||
"测试已在运行中": "Test is already running",
|
"测试已在运行中": "Test is already running",
|
||||||
"响应时间 %.2fs 超过阈值 %.2fs": "Response time %.2fs exceeds threshold %.2fs",
|
"响应时间 %.2fs 超过阈值 %.2fs": "Response time %.2fs exceeds threshold %.2fs",
|
||||||
"通道测试完成": "Channel test completed",
|
"渠道测试完成": "Channel test completed",
|
||||||
"通道测试完成,如果没有收到禁用通知,说明所有通道都正常": "Channel test completed, if you have not received the disable notification, it means that all channels are normal",
|
"渠道测试完成,如果没有收到禁用通知,说明所有渠道都正常": "Channel test completed, if you have not received the disable notification, it means that all channels are normal",
|
||||||
"无法连接至 GitHub 服务器,请稍后重试!": "Unable to connect to GitHub server, please try again later!",
|
"无法连接至 GitHub 服务器,请稍后重试!": "Unable to connect to GitHub server, please try again later!",
|
||||||
"返回值非法,用户字段为空,请稍后重试!": "The return value is illegal, the user field is empty, please try again later!",
|
"返回值非法,用户字段为空,请稍后重试!": "The return value is illegal, the user field is empty, please try again later!",
|
||||||
"管理员未开启通过 GitHub 登录以及注册": "The administrator did not turn on login and registration via GitHub",
|
"管理员未开启通过 GitHub 登录以及注册": "The administrator did not turn on login and registration via GitHub",
|
||||||
@ -119,11 +119,11 @@
|
|||||||
" 个月 ": " M ",
|
" 个月 ": " M ",
|
||||||
" 年 ": " y ",
|
" 年 ": " y ",
|
||||||
"未测试": "Not tested",
|
"未测试": "Not tested",
|
||||||
"通道 ${name} 测试成功,耗时 ${time.toFixed(2)} 秒。": "Channel ${name} test succeeded, time consumed ${time.toFixed(2)} s.",
|
"渠道 ${name} 测试成功,耗时 ${time.toFixed(2)} 秒。": "Channel ${name} test succeeded, time consumed ${time.toFixed(2)} s.",
|
||||||
"已成功开始测试所有通道,请刷新页面查看结果。": "All channels have been successfully tested, please refresh the page to view the results.",
|
"已成功开始测试所有渠道,请刷新页面查看结果。": "All channels have been successfully tested, please refresh the page to view the results.",
|
||||||
"已成功开始测试所有已启用通道,请刷新页面查看结果。": "All enabled channels have been successfully tested, please refresh the page to view the results.",
|
"已成功开始测试所有已启用渠道,请刷新页面查看结果。": "All enabled channels have been successfully tested, please refresh the page to view the results.",
|
||||||
"通道 ${name} 余额更新成功!": "Channel ${name} balance updated successfully!",
|
"渠道 ${name} 余额更新成功!": "Channel ${name} balance updated successfully!",
|
||||||
"已更新完毕所有已启用通道余额!": "The balance of all enabled channels has been updated!",
|
"已更新完毕所有已启用渠道余额!": "The balance of all enabled channels has been updated!",
|
||||||
"搜索渠道的 ID,名称和密钥 ...": "Search for channel ID, name and key ...",
|
"搜索渠道的 ID,名称和密钥 ...": "Search for channel ID, name and key ...",
|
||||||
"名称": "Name",
|
"名称": "Name",
|
||||||
"分组": "Group",
|
"分组": "Group",
|
||||||
@ -141,9 +141,9 @@
|
|||||||
"启用": "Enable",
|
"启用": "Enable",
|
||||||
"编辑": "Edit",
|
"编辑": "Edit",
|
||||||
"添加新的渠道": "Add a new channel",
|
"添加新的渠道": "Add a new channel",
|
||||||
"测试所有通道": "Test all channels",
|
"测试所有渠道": "Test all channels",
|
||||||
"测试所有已启用通道": "Test all enabled channels",
|
"测试所有已启用渠道": "Test all enabled channels",
|
||||||
"更新所有已启用通道余额": "Update the balance of all enabled channels",
|
"更新所有已启用渠道余额": "Update the balance of all enabled channels",
|
||||||
"刷新": "Refresh",
|
"刷新": "Refresh",
|
||||||
"处理中...": "Processing...",
|
"处理中...": "Processing...",
|
||||||
"绑定成功!": "Binding succeeded!",
|
"绑定成功!": "Binding succeeded!",
|
||||||
@ -207,11 +207,11 @@
|
|||||||
"监控设置": "Monitoring Settings",
|
"监控设置": "Monitoring Settings",
|
||||||
"最长响应时间": "Longest Response Time",
|
"最长响应时间": "Longest Response Time",
|
||||||
"单位秒": "Unit in seconds",
|
"单位秒": "Unit in seconds",
|
||||||
"当运行通道全部测试时": "When all operating channels are tested",
|
"当运行渠道全部测试时": "When all operating channels are tested",
|
||||||
"超过此时间将自动禁用通道": "Channels will be automatically disabled if this time is exceeded",
|
"超过此时间将自动禁用渠道": "Channels will be automatically disabled if this time is exceeded",
|
||||||
"额度提醒阈值": "Quota reminder threshold",
|
"额度提醒阈值": "Quota reminder threshold",
|
||||||
"低于此额度时将发送邮件提醒用户": "Email will be sent to remind users when the quota is below this",
|
"低于此额度时将发送邮件提醒用户": "Email will be sent to remind users when the quota is below this",
|
||||||
"失败时自动禁用通道": "Automatically disable the channel when it fails",
|
"失败时自动禁用渠道": "Automatically disable the channel when it fails",
|
||||||
"保存监控设置": "Save Monitoring Settings",
|
"保存监控设置": "Save Monitoring Settings",
|
||||||
"额度设置": "Quota Settings",
|
"额度设置": "Quota Settings",
|
||||||
"新用户初始额度": "Initial quota for new users",
|
"新用户初始额度": "Initial quota for new users",
|
||||||
@ -405,7 +405,7 @@
|
|||||||
"镜像": "Mirror",
|
"镜像": "Mirror",
|
||||||
"请输入镜像站地址,格式为:https://domain.com,可不填,不填则使用渠道默认值": "Please enter the mirror site address, the format is: https://domain.com, it can be left blank, if left blank, the default value of the channel will be used",
|
"请输入镜像站地址,格式为:https://domain.com,可不填,不填则使用渠道默认值": "Please enter the mirror site address, the format is: https://domain.com, it can be left blank, if left blank, the default value of the channel will be used",
|
||||||
"模型": "Model",
|
"模型": "Model",
|
||||||
"请选择该通道所支持的模型": "Please select the model supported by the channel",
|
"请选择该渠道所支持的模型": "Please select the model supported by the channel",
|
||||||
"填入基础模型": "Fill in the basic model",
|
"填入基础模型": "Fill in the basic model",
|
||||||
"填入所有模型": "Fill in all models",
|
"填入所有模型": "Fill in all models",
|
||||||
"清除所有模型": "Clear all models",
|
"清除所有模型": "Clear all models",
|
||||||
@ -515,7 +515,7 @@
|
|||||||
"请输入自定义渠道的 Base URL": "Please enter the Base URL of the custom channel",
|
"请输入自定义渠道的 Base URL": "Please enter the Base URL of the custom channel",
|
||||||
"Homepage URL 填": "Fill in the Homepage URL",
|
"Homepage URL 填": "Fill in the Homepage URL",
|
||||||
"Authorization callback URL 填": "Fill in the Authorization callback URL",
|
"Authorization callback URL 填": "Fill in the Authorization callback URL",
|
||||||
"请为通道命名": "Please name the channel",
|
"请为渠道命名": "Please name the channel",
|
||||||
"此项可选,用于修改请求体中的模型名称,为一个 JSON 字符串,键为请求中模型名称,值为要替换的模型名称,例如:": "This is optional, used to modify the model name in the request body, it's a JSON string, the key is the model name in the request, and the value is the model name to be replaced, for example:",
|
"此项可选,用于修改请求体中的模型名称,为一个 JSON 字符串,键为请求中模型名称,值为要替换的模型名称,例如:": "This is optional, used to modify the model name in the request body, it's a JSON string, the key is the model name in the request, and the value is the model name to be replaced, for example:",
|
||||||
"模型重定向": "Model redirection",
|
"模型重定向": "Model redirection",
|
||||||
"请输入渠道对应的鉴权密钥": "Please enter the authentication key corresponding to the channel",
|
"请输入渠道对应的鉴权密钥": "Please enter the authentication key corresponding to the channel",
|
||||||
|
@ -31,17 +31,17 @@ func notifyRootUser(subject string, content string) {
|
|||||||
func DisableChannel(channelId int, channelName string, reason string) {
|
func DisableChannel(channelId int, channelName string, reason string) {
|
||||||
model.UpdateChannelStatusById(channelId, common.ChannelStatusAutoDisabled)
|
model.UpdateChannelStatusById(channelId, common.ChannelStatusAutoDisabled)
|
||||||
logger.SysLog(fmt.Sprintf("channel #%d has been disabled: %s", channelId, reason))
|
logger.SysLog(fmt.Sprintf("channel #%d has been disabled: %s", channelId, reason))
|
||||||
subject := fmt.Sprintf("通道「%s」(#%d)已被禁用", channelName, channelId)
|
subject := fmt.Sprintf("渠道「%s」(#%d)已被禁用", channelName, channelId)
|
||||||
content := fmt.Sprintf("通道「%s」(#%d)已被禁用,原因:%s", channelName, channelId, reason)
|
content := fmt.Sprintf("渠道「%s」(#%d)已被禁用,原因:%s", channelName, channelId, reason)
|
||||||
notifyRootUser(subject, content)
|
notifyRootUser(subject, content)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MetricDisableChannel(channelId int, successRate float64) {
|
func MetricDisableChannel(channelId int, successRate float64) {
|
||||||
model.UpdateChannelStatusById(channelId, common.ChannelStatusAutoDisabled)
|
model.UpdateChannelStatusById(channelId, common.ChannelStatusAutoDisabled)
|
||||||
logger.SysLog(fmt.Sprintf("channel #%d has been disabled due to low success rate: %.2f", channelId, successRate*100))
|
logger.SysLog(fmt.Sprintf("channel #%d has been disabled due to low success rate: %.2f", channelId, successRate*100))
|
||||||
subject := fmt.Sprintf("通道 #%d 已被禁用", channelId)
|
subject := fmt.Sprintf("渠道 #%d 已被禁用", channelId)
|
||||||
content := fmt.Sprintf("该渠道在最近 %d 次调用中成功率为 %.2f%%,低于阈值 %.2f%%,因此被系统自动禁用。",
|
content := fmt.Sprintf("该渠道(#%d)在最近 %d 次调用中成功率为 %.2f%%,低于阈值 %.2f%%,因此被系统自动禁用。",
|
||||||
config.MetricQueueSize, successRate*100, config.MetricSuccessRateThreshold*100)
|
channelId, config.MetricQueueSize, successRate*100, config.MetricSuccessRateThreshold*100)
|
||||||
notifyRootUser(subject, content)
|
notifyRootUser(subject, content)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ func MetricDisableChannel(channelId int, successRate float64) {
|
|||||||
func EnableChannel(channelId int, channelName string) {
|
func EnableChannel(channelId int, channelName string) {
|
||||||
model.UpdateChannelStatusById(channelId, common.ChannelStatusEnabled)
|
model.UpdateChannelStatusById(channelId, common.ChannelStatusEnabled)
|
||||||
logger.SysLog(fmt.Sprintf("channel #%d has been enabled", channelId))
|
logger.SysLog(fmt.Sprintf("channel #%d has been enabled", channelId))
|
||||||
subject := fmt.Sprintf("通道「%s」(#%d)已被启用", channelName, channelId)
|
subject := fmt.Sprintf("渠道「%s」(#%d)已被启用", channelName, channelId)
|
||||||
content := fmt.Sprintf("通道「%s」(#%d)已被启用", channelName, channelId)
|
content := fmt.Sprintf("渠道「%s」(#%d)已被启用", channelName, channelId)
|
||||||
notifyRootUser(subject, content)
|
notifyRootUser(subject, content)
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ package gemini
|
|||||||
// https://ai.google.dev/models/gemini
|
// https://ai.google.dev/models/gemini
|
||||||
|
|
||||||
var ModelList = []string{
|
var ModelList = []string{
|
||||||
"gemini-pro", "gemini-1.0-pro-001",
|
"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",
|
"embedding-001",
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,14 @@ package ollama
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/songquanpeng/one-api/relay/channel"
|
"github.com/songquanpeng/one-api/relay/channel"
|
||||||
"github.com/songquanpeng/one-api/relay/constant"
|
"github.com/songquanpeng/one-api/relay/constant"
|
||||||
"github.com/songquanpeng/one-api/relay/model"
|
"github.com/songquanpeng/one-api/relay/model"
|
||||||
"github.com/songquanpeng/one-api/relay/util"
|
"github.com/songquanpeng/one-api/relay/util"
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Adaptor struct {
|
type Adaptor struct {
|
||||||
@ -22,6 +23,9 @@ func (a *Adaptor) Init(meta *util.RelayMeta) {
|
|||||||
func (a *Adaptor) GetRequestURL(meta *util.RelayMeta) (string, error) {
|
func (a *Adaptor) GetRequestURL(meta *util.RelayMeta) (string, error) {
|
||||||
// https://github.com/ollama/ollama/blob/main/docs/api.md
|
// https://github.com/ollama/ollama/blob/main/docs/api.md
|
||||||
fullRequestURL := fmt.Sprintf("%s/api/chat", meta.BaseURL)
|
fullRequestURL := fmt.Sprintf("%s/api/chat", meta.BaseURL)
|
||||||
|
if meta.Mode == constant.RelayModeEmbeddings {
|
||||||
|
fullRequestURL = fmt.Sprintf("%s/api/embeddings", meta.BaseURL)
|
||||||
|
}
|
||||||
return fullRequestURL, nil
|
return fullRequestURL, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +41,8 @@ func (a *Adaptor) ConvertRequest(c *gin.Context, relayMode int, request *model.G
|
|||||||
}
|
}
|
||||||
switch relayMode {
|
switch relayMode {
|
||||||
case constant.RelayModeEmbeddings:
|
case constant.RelayModeEmbeddings:
|
||||||
return nil, errors.New("not supported")
|
ollamaEmbeddingRequest := ConvertEmbeddingRequest(*request)
|
||||||
|
return ollamaEmbeddingRequest, nil
|
||||||
default:
|
default:
|
||||||
return ConvertRequest(*request), nil
|
return ConvertRequest(*request), nil
|
||||||
}
|
}
|
||||||
@ -51,7 +56,12 @@ func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, meta *util.Rel
|
|||||||
if meta.IsStream {
|
if meta.IsStream {
|
||||||
err, usage = StreamHandler(c, resp)
|
err, usage = StreamHandler(c, resp)
|
||||||
} else {
|
} else {
|
||||||
err, usage = Handler(c, resp)
|
switch meta.Mode {
|
||||||
|
case constant.RelayModeEmbeddings:
|
||||||
|
err, usage = EmbeddingHandler(c, resp)
|
||||||
|
default:
|
||||||
|
err, usage = Handler(c, resp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,10 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/songquanpeng/one-api/common"
|
"github.com/songquanpeng/one-api/common"
|
||||||
"github.com/songquanpeng/one-api/common/helper"
|
"github.com/songquanpeng/one-api/common/helper"
|
||||||
@ -12,9 +16,6 @@ import (
|
|||||||
"github.com/songquanpeng/one-api/relay/channel/openai"
|
"github.com/songquanpeng/one-api/relay/channel/openai"
|
||||||
"github.com/songquanpeng/one-api/relay/constant"
|
"github.com/songquanpeng/one-api/relay/constant"
|
||||||
"github.com/songquanpeng/one-api/relay/model"
|
"github.com/songquanpeng/one-api/relay/model"
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ConvertRequest(request model.GeneralOpenAIRequest) *ChatRequest {
|
func ConvertRequest(request model.GeneralOpenAIRequest) *ChatRequest {
|
||||||
@ -139,6 +140,64 @@ func StreamHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusC
|
|||||||
return nil, &usage
|
return nil, &usage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ConvertEmbeddingRequest(request model.GeneralOpenAIRequest) *EmbeddingRequest {
|
||||||
|
return &EmbeddingRequest{
|
||||||
|
Model: request.Model,
|
||||||
|
Prompt: strings.Join(request.ParseInput(), " "),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func EmbeddingHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusCode, *model.Usage) {
|
||||||
|
var ollamaResponse EmbeddingResponse
|
||||||
|
err := json.NewDecoder(resp.Body).Decode(&ollamaResponse)
|
||||||
|
if err != nil {
|
||||||
|
return openai.ErrorWrapper(err, "unmarshal_response_body_failed", http.StatusInternalServerError), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = resp.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if ollamaResponse.Error != "" {
|
||||||
|
return &model.ErrorWithStatusCode{
|
||||||
|
Error: model.Error{
|
||||||
|
Message: ollamaResponse.Error,
|
||||||
|
Type: "ollama_error",
|
||||||
|
Param: "",
|
||||||
|
Code: "ollama_error",
|
||||||
|
},
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fullTextResponse := embeddingResponseOllama2OpenAI(&ollamaResponse)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func embeddingResponseOllama2OpenAI(response *EmbeddingResponse) *openai.EmbeddingResponse {
|
||||||
|
openAIEmbeddingResponse := openai.EmbeddingResponse{
|
||||||
|
Object: "list",
|
||||||
|
Data: make([]openai.EmbeddingResponseItem, 0, 1),
|
||||||
|
Model: "text-embedding-v1",
|
||||||
|
Usage: model.Usage{TotalTokens: 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
openAIEmbeddingResponse.Data = append(openAIEmbeddingResponse.Data, openai.EmbeddingResponseItem{
|
||||||
|
Object: `embedding`,
|
||||||
|
Index: 0,
|
||||||
|
Embedding: response.Embedding,
|
||||||
|
})
|
||||||
|
return &openAIEmbeddingResponse
|
||||||
|
}
|
||||||
|
|
||||||
func Handler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusCode, *model.Usage) {
|
func Handler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusCode, *model.Usage) {
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
var ollamaResponse ChatResponse
|
var ollamaResponse ChatResponse
|
||||||
|
@ -35,3 +35,13 @@ type ChatResponse struct {
|
|||||||
EvalDuration int `json:"eval_duration,omitempty"`
|
EvalDuration int `json:"eval_duration,omitempty"`
|
||||||
Error string `json:"error,omitempty"`
|
Error string `json:"error,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type EmbeddingRequest struct {
|
||||||
|
Model string `json:"model"`
|
||||||
|
Prompt string `json:"prompt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type EmbeddingResponse struct {
|
||||||
|
Error string `json:"error,omitempty"`
|
||||||
|
Embedding []float64 `json:"embedding,omitempty"`
|
||||||
|
}
|
||||||
|
@ -31,11 +31,8 @@ func (a *Adaptor) GetRequestURL(meta *util.RelayMeta) (string, error) {
|
|||||||
task := strings.TrimPrefix(requestURL, "/v1/")
|
task := strings.TrimPrefix(requestURL, "/v1/")
|
||||||
model_ := meta.ActualModelName
|
model_ := meta.ActualModelName
|
||||||
model_ = strings.Replace(model_, ".", "", -1)
|
model_ = strings.Replace(model_, ".", "", -1)
|
||||||
// https://github.com/songquanpeng/one-api/issues/67
|
//https://github.com/songquanpeng/one-api/issues/1191
|
||||||
model_ = strings.TrimSuffix(model_, "-0301")
|
// {your endpoint}/openai/deployments/{your azure_model}/chat/completions?api-version={api_version}
|
||||||
model_ = strings.TrimSuffix(model_, "-0314")
|
|
||||||
model_ = strings.TrimSuffix(model_, "-0613")
|
|
||||||
|
|
||||||
requestURL = fmt.Sprintf("/openai/deployments/%s/%s", model_, task)
|
requestURL = fmt.Sprintf("/openai/deployments/%s/%s", model_, task)
|
||||||
return util.GetFullRequestURL(meta.BaseURL, requestURL, meta.ChannelType), nil
|
return util.GetFullRequestURL(meta.BaseURL, requestURL, meta.ChannelType), nil
|
||||||
case common.ChannelTypeMinimax:
|
case common.ChannelTypeMinimax:
|
||||||
|
@ -121,7 +121,7 @@ func StreamHandler(c *gin.Context, textRequest model.GeneralOpenAIRequest, appId
|
|||||||
domain, authUrl := getXunfeiAuthUrl(c, apiKey, apiSecret, textRequest.Model)
|
domain, authUrl := getXunfeiAuthUrl(c, apiKey, apiSecret, textRequest.Model)
|
||||||
dataChan, stopChan, err := xunfeiMakeRequest(textRequest, domain, authUrl, appId)
|
dataChan, stopChan, err := xunfeiMakeRequest(textRequest, domain, authUrl, appId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return openai.ErrorWrapper(err, "make xunfei request err", http.StatusInternalServerError), nil
|
return openai.ErrorWrapper(err, "xunfei_request_failed", http.StatusInternalServerError), nil
|
||||||
}
|
}
|
||||||
common.SetEventStreamHeaders(c)
|
common.SetEventStreamHeaders(c)
|
||||||
var usage model.Usage
|
var usage model.Usage
|
||||||
@ -151,7 +151,7 @@ func Handler(c *gin.Context, textRequest model.GeneralOpenAIRequest, appId strin
|
|||||||
domain, authUrl := getXunfeiAuthUrl(c, apiKey, apiSecret, textRequest.Model)
|
domain, authUrl := getXunfeiAuthUrl(c, apiKey, apiSecret, textRequest.Model)
|
||||||
dataChan, stopChan, err := xunfeiMakeRequest(textRequest, domain, authUrl, appId)
|
dataChan, stopChan, err := xunfeiMakeRequest(textRequest, domain, authUrl, appId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return openai.ErrorWrapper(err, "make xunfei request err", http.StatusInternalServerError), nil
|
return openai.ErrorWrapper(err, "xunfei_request_failed", http.StatusInternalServerError), nil
|
||||||
}
|
}
|
||||||
var usage model.Usage
|
var usage model.Usage
|
||||||
var content string
|
var content string
|
||||||
@ -171,11 +171,7 @@ func Handler(c *gin.Context, textRequest model.GeneralOpenAIRequest, appId strin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(xunfeiResponse.Payload.Choices.Text) == 0 {
|
if len(xunfeiResponse.Payload.Choices.Text) == 0 {
|
||||||
xunfeiResponse.Payload.Choices.Text = []ChatResponseTextItem{
|
return openai.ErrorWrapper(err, "xunfei_empty_response_detected", http.StatusInternalServerError), nil
|
||||||
{
|
|
||||||
Content: "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
xunfeiResponse.Payload.Choices.Text[0].Content = content
|
xunfeiResponse.Payload.Choices.Text[0].Content = content
|
||||||
|
|
||||||
@ -202,15 +198,21 @@ func xunfeiMakeRequest(textRequest model.GeneralOpenAIRequest, domain, authUrl,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
_, msg, err := conn.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
dataChan := make(chan ChatResponse)
|
dataChan := make(chan ChatResponse)
|
||||||
stopChan := make(chan bool)
|
stopChan := make(chan bool)
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
_, msg, err := conn.ReadMessage()
|
if msg == nil {
|
||||||
if err != nil {
|
_, msg, err = conn.ReadMessage()
|
||||||
logger.SysError("error reading stream response: " + err.Error())
|
if err != nil {
|
||||||
break
|
logger.SysError("error reading stream response: " + err.Error())
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var response ChatResponse
|
var response ChatResponse
|
||||||
err = json.Unmarshal(msg, &response)
|
err = json.Unmarshal(msg, &response)
|
||||||
@ -218,6 +220,7 @@ func xunfeiMakeRequest(textRequest model.GeneralOpenAIRequest, domain, authUrl,
|
|||||||
logger.SysError("error unmarshalling stream response: " + err.Error())
|
logger.SysError("error unmarshalling stream response: " + err.Error())
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
msg = nil
|
||||||
dataChan <- response
|
dataChan <- response
|
||||||
if response.Payload.Choices.Status == 2 {
|
if response.Payload.Choices.Status == 2 {
|
||||||
err := conn.Close()
|
err := conn.Close()
|
||||||
|
@ -83,6 +83,24 @@ func RelayAudioHelper(c *gin.Context, relayMode int) *relaymodel.ErrorWithStatus
|
|||||||
return openai.ErrorWrapper(err, "pre_consume_token_quota_failed", http.StatusForbidden)
|
return openai.ErrorWrapper(err, "pre_consume_token_quota_failed", http.StatusForbidden)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
succeed := false
|
||||||
|
defer func() {
|
||||||
|
if succeed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if preConsumedQuota > 0 {
|
||||||
|
// we need to roll back the pre-consumed quota
|
||||||
|
defer func(ctx context.Context) {
|
||||||
|
go func() {
|
||||||
|
// negative means add quota back for token & user
|
||||||
|
err := model.PostConsumeTokenQuota(tokenId, -preConsumedQuota)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(ctx, fmt.Sprintf("error rollback pre-consumed quota: %s", err.Error()))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}(c.Request.Context())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// map model name
|
// map model name
|
||||||
modelMapping := c.GetString("model_mapping")
|
modelMapping := c.GetString("model_mapping")
|
||||||
@ -193,20 +211,9 @@ func RelayAudioHelper(c *gin.Context, relayMode int) *relaymodel.ErrorWithStatus
|
|||||||
resp.Body = io.NopCloser(bytes.NewBuffer(responseBody))
|
resp.Body = io.NopCloser(bytes.NewBuffer(responseBody))
|
||||||
}
|
}
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
if preConsumedQuota > 0 {
|
|
||||||
// we need to roll back the pre-consumed quota
|
|
||||||
defer func(ctx context.Context) {
|
|
||||||
go func() {
|
|
||||||
// negative means add quota back for token & user
|
|
||||||
err := model.PostConsumeTokenQuota(tokenId, -preConsumedQuota)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(ctx, fmt.Sprintf("error rollback pre-consumed quota: %s", err.Error()))
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}(c.Request.Context())
|
|
||||||
}
|
|
||||||
return util.RelayErrorHandler(resp)
|
return util.RelayErrorHandler(resp)
|
||||||
}
|
}
|
||||||
|
succeed = true
|
||||||
quotaDelta := quota - preConsumedQuota
|
quotaDelta := quota - preConsumedQuota
|
||||||
defer func(ctx context.Context) {
|
defer func(ctx context.Context) {
|
||||||
go util.PostConsumeQuota(ctx, tokenId, quotaDelta, quota, userId, channelId, modelRatio, groupRatio, audioModel, tokenName)
|
go util.PostConsumeQuota(ctx, tokenId, quotaDelta, quota, userId, channelId, modelRatio, groupRatio, audioModel, tokenName)
|
||||||
|
@ -437,7 +437,7 @@ const ChannelsTable = () => {
|
|||||||
if (success) {
|
if (success) {
|
||||||
record.response_time = time * 1000;
|
record.response_time = time * 1000;
|
||||||
record.test_time = Date.now() / 1000;
|
record.test_time = Date.now() / 1000;
|
||||||
showInfo(`通道 ${record.name} 测试成功,耗时 ${time.toFixed(2)} 秒。`);
|
showInfo(`渠道 ${record.name} 测试成功,耗时 ${time.toFixed(2)} 秒。`);
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
}
|
}
|
||||||
@ -447,7 +447,7 @@ const ChannelsTable = () => {
|
|||||||
const res = await API.get(`/api/channel/test?scope=${scope}`);
|
const res = await API.get(`/api/channel/test?scope=${scope}`);
|
||||||
const { success, message } = res.data;
|
const { success, message } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
showInfo('已成功开始测试通道,请刷新页面查看结果。');
|
showInfo('已成功开始测试渠道,请刷新页面查看结果。');
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
}
|
}
|
||||||
@ -470,7 +470,7 @@ const ChannelsTable = () => {
|
|||||||
if (success) {
|
if (success) {
|
||||||
record.balance = balance;
|
record.balance = balance;
|
||||||
record.balance_updated_time = Date.now() / 1000;
|
record.balance_updated_time = Date.now() / 1000;
|
||||||
showInfo(`通道 ${record.name} 余额更新成功!`);
|
showInfo(`渠道 ${record.name} 余额更新成功!`);
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
}
|
}
|
||||||
@ -481,7 +481,7 @@ const ChannelsTable = () => {
|
|||||||
const res = await API.get(`/api/channel/update_balance`);
|
const res = await API.get(`/api/channel/update_balance`);
|
||||||
const { success, message } = res.data;
|
const { success, message } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
showInfo('已更新完毕所有已启用通道余额!');
|
showInfo('已更新完毕所有已启用渠道余额!');
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
}
|
}
|
||||||
@ -490,7 +490,7 @@ const ChannelsTable = () => {
|
|||||||
|
|
||||||
const batchDeleteChannels = async () => {
|
const batchDeleteChannels = async () => {
|
||||||
if (selectedChannels.length === 0) {
|
if (selectedChannels.length === 0) {
|
||||||
showError('请先选择要删除的通道!');
|
showError('请先选择要删除的渠道!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@ -501,7 +501,7 @@ const ChannelsTable = () => {
|
|||||||
const res = await API.post(`/api/channel/batch`, { ids: ids });
|
const res = await API.post(`/api/channel/batch`, { ids: ids });
|
||||||
const { success, message, data } = res.data;
|
const { success, message, data } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
showSuccess(`已删除 ${data} 个通道!`);
|
showSuccess(`已删除 ${data} 个渠道!`);
|
||||||
await refresh();
|
await refresh();
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
@ -513,7 +513,7 @@ const ChannelsTable = () => {
|
|||||||
const res = await API.post(`/api/channel/fix`);
|
const res = await API.post(`/api/channel/fix`);
|
||||||
const { success, message, data } = res.data;
|
const { success, message, data } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
showSuccess(`已修复 ${data} 个通道!`);
|
showSuccess(`已修复 ${data} 个渠道!`);
|
||||||
await refresh();
|
await refresh();
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
@ -633,7 +633,7 @@ const ChannelsTable = () => {
|
|||||||
onConfirm={() => { testChannels("all") }}
|
onConfirm={() => { testChannels("all") }}
|
||||||
position={isMobile() ? 'top' : 'left'}
|
position={isMobile() ? 'top' : 'left'}
|
||||||
>
|
>
|
||||||
<Button theme="light" type="warning" style={{ marginRight: 8 }}>测试所有通道</Button>
|
<Button theme="light" type="warning" style={{ marginRight: 8 }}>测试所有渠道</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="确定?"
|
title="确定?"
|
||||||
@ -648,16 +648,16 @@ const ChannelsTable = () => {
|
|||||||
okType={'secondary'}
|
okType={'secondary'}
|
||||||
onConfirm={updateAllChannelsBalance}
|
onConfirm={updateAllChannelsBalance}
|
||||||
>
|
>
|
||||||
<Button theme="light" type="secondary" style={{ marginRight: 8 }}>更新所有已启用通道余额</Button>
|
<Button theme="light" type="secondary" style={{ marginRight: 8 }}>更新所有已启用渠道余额</Button>
|
||||||
</Popconfirm> */}
|
</Popconfirm> */}
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="确定是否要删除禁用通道?"
|
title="确定是否要删除禁用渠道?"
|
||||||
content="此修改将不可逆"
|
content="此修改将不可逆"
|
||||||
okType={'danger'}
|
okType={'danger'}
|
||||||
onConfirm={deleteAllDisabledChannels}
|
onConfirm={deleteAllDisabledChannels}
|
||||||
position={isMobile() ? 'top' : 'left'}
|
position={isMobile() ? 'top' : 'left'}
|
||||||
>
|
>
|
||||||
<Button theme="light" type="danger" style={{ marginRight: 8 }}>删除禁用通道</Button>
|
<Button theme="light" type="danger" style={{ marginRight: 8 }}>删除禁用渠道</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
|
|
||||||
<Button theme="light" type="primary" style={{ marginRight: 8 }} onClick={refresh}>刷新</Button>
|
<Button theme="light" type="primary" style={{ marginRight: 8 }} onClick={refresh}>刷新</Button>
|
||||||
@ -673,7 +673,7 @@ const ChannelsTable = () => {
|
|||||||
setEnableBatchDelete(v);
|
setEnableBatchDelete(v);
|
||||||
}}></Switch>
|
}}></Switch>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="确定是否要删除所选通道?"
|
title="确定是否要删除所选渠道?"
|
||||||
content="此修改将不可逆"
|
content="此修改将不可逆"
|
||||||
okType={'danger'}
|
okType={'danger'}
|
||||||
onConfirm={batchDeleteChannels}
|
onConfirm={batchDeleteChannels}
|
||||||
@ -681,7 +681,7 @@ const ChannelsTable = () => {
|
|||||||
position={'top'}
|
position={'top'}
|
||||||
>
|
>
|
||||||
<Button disabled={!enableBatchDelete} theme="light" type="danger"
|
<Button disabled={!enableBatchDelete} theme="light" type="danger"
|
||||||
style={{ marginRight: 8 }}>删除所选通道</Button>
|
style={{ marginRight: 8 }}>删除所选渠道</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="确定是否要修复数据库一致性?"
|
title="确定是否要修复数据库一致性?"
|
||||||
|
@ -261,7 +261,7 @@ const OperationSetting = () => {
|
|||||||
value={inputs.ChannelDisableThreshold}
|
value={inputs.ChannelDisableThreshold}
|
||||||
type='number'
|
type='number'
|
||||||
min='0'
|
min='0'
|
||||||
placeholder='单位秒,当运行通道全部测试时,超过此时间将自动禁用通道'
|
placeholder='单位秒,当运行渠道全部测试时,超过此时间将自动禁用渠道'
|
||||||
/>
|
/>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='额度提醒阈值'
|
label='额度提醒阈值'
|
||||||
@ -277,13 +277,13 @@ const OperationSetting = () => {
|
|||||||
<Form.Group inline>
|
<Form.Group inline>
|
||||||
<Form.Checkbox
|
<Form.Checkbox
|
||||||
checked={inputs.AutomaticDisableChannelEnabled === 'true'}
|
checked={inputs.AutomaticDisableChannelEnabled === 'true'}
|
||||||
label='失败时自动禁用通道'
|
label='失败时自动禁用渠道'
|
||||||
name='AutomaticDisableChannelEnabled'
|
name='AutomaticDisableChannelEnabled'
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
<Form.Checkbox
|
<Form.Checkbox
|
||||||
checked={inputs.AutomaticEnableChannelEnabled === 'true'}
|
checked={inputs.AutomaticEnableChannelEnabled === 'true'}
|
||||||
label='成功时自动启用通道'
|
label='成功时自动启用渠道'
|
||||||
name='AutomaticEnableChannelEnabled'
|
name='AutomaticEnableChannelEnabled'
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
|
@ -51,7 +51,7 @@ const Register = () => {
|
|||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<Grid item container direction="column" alignItems="center" xs={12}>
|
<Grid item container direction="column" alignItems="center" xs={12}>
|
||||||
<Typography component={Link} to="/login" variant="subtitle1" sx={{ textDecoration: 'none' }}>
|
<Typography component={Link} to="/login" variant="subtitle1" sx={{ textDecoration: 'none' }}>
|
||||||
已经有帐号了?点击登录
|
已经有帐号了?点击登录
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -296,7 +296,7 @@ const RegisterForm = ({ ...others }) => {
|
|||||||
<Box sx={{ mt: 2 }}>
|
<Box sx={{ mt: 2 }}>
|
||||||
<AnimateButton>
|
<AnimateButton>
|
||||||
<Button disableElevation disabled={isSubmitting} fullWidth size="large" type="submit" variant="contained" color="primary">
|
<Button disableElevation disabled={isSubmitting} fullWidth size="large" type="submit" variant="contained" color="primary">
|
||||||
Sign up
|
注册
|
||||||
</Button>
|
</Button>
|
||||||
</AnimateButton>
|
</AnimateButton>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -93,7 +93,7 @@ export default function ChannelTableRow({
|
|||||||
test_time: Date.now() / 1000,
|
test_time: Date.now() / 1000,
|
||||||
response_time: time * 1000,
|
response_time: time * 1000,
|
||||||
});
|
});
|
||||||
showInfo(`通道 ${item.name} 测试成功,耗时 ${time.toFixed(2)} 秒。`);
|
showInfo(`渠道 ${item.name} 测试成功,耗时 ${time.toFixed(2)} 秒。`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -243,9 +243,9 @@ export default function ChannelTableRow({
|
|||||||
</Popover>
|
</Popover>
|
||||||
|
|
||||||
<Dialog open={openDelete} onClose={handleDeleteClose}>
|
<Dialog open={openDelete} onClose={handleDeleteClose}>
|
||||||
<DialogTitle>删除通道</DialogTitle>
|
<DialogTitle>删除渠道</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogContentText>是否删除通道 {item.name}?</DialogContentText>
|
<DialogContentText>是否删除渠道 {item.name}?</DialogContentText>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button onClick={handleDeleteClose}>关闭</Button>
|
<Button onClick={handleDeleteClose}>关闭</Button>
|
||||||
|
@ -135,7 +135,7 @@ export default function ChannelPage() {
|
|||||||
const res = await API.get(`/api/channel/test`);
|
const res = await API.get(`/api/channel/test`);
|
||||||
const { success, message } = res.data;
|
const { success, message } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
showInfo('已成功开始测试所有通道,请刷新页面查看结果。');
|
showInfo('已成功开始测试所有渠道,请刷新页面查看结果。');
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
}
|
}
|
||||||
@ -159,7 +159,7 @@ export default function ChannelPage() {
|
|||||||
const res = await API.get(`/api/channel/update_balance`);
|
const res = await API.get(`/api/channel/update_balance`);
|
||||||
const { success, message } = res.data;
|
const { success, message } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
showInfo('已更新完毕所有已启用通道余额!');
|
showInfo('已更新完毕所有已启用渠道余额!');
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
}
|
}
|
||||||
|
@ -371,7 +371,7 @@ const OperationSetting = () => {
|
|||||||
value={inputs.ChannelDisableThreshold}
|
value={inputs.ChannelDisableThreshold}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
label="最长响应时间"
|
label="最长响应时间"
|
||||||
placeholder="单位秒,当运行通道全部测试时,超过此时间将自动禁用通道"
|
placeholder="单位秒,当运行渠道全部测试时,超过此时间将自动禁用渠道"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@ -392,7 +392,7 @@ const OperationSetting = () => {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
</Stack>
|
</Stack>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
label="失败时自动禁用通道"
|
label="失败时自动禁用渠道"
|
||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={inputs.AutomaticDisableChannelEnabled === "true"}
|
checked={inputs.AutomaticDisableChannelEnabled === "true"}
|
||||||
@ -402,7 +402,7 @@ const OperationSetting = () => {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
label="成功时自动启用通道"
|
label="成功时自动启用渠道"
|
||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={inputs.AutomaticEnableChannelEnabled === "true"}
|
checked={inputs.AutomaticEnableChannelEnabled === "true"}
|
||||||
|
@ -234,7 +234,7 @@ const ChannelsTable = () => {
|
|||||||
newChannels[realIdx].response_time = time * 1000;
|
newChannels[realIdx].response_time = time * 1000;
|
||||||
newChannels[realIdx].test_time = Date.now() / 1000;
|
newChannels[realIdx].test_time = Date.now() / 1000;
|
||||||
setChannels(newChannels);
|
setChannels(newChannels);
|
||||||
showInfo(`通道 ${name} 测试成功,耗时 ${time.toFixed(2)} 秒。`);
|
showInfo(`渠道 ${name} 测试成功,耗时 ${time.toFixed(2)} 秒。`);
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
}
|
}
|
||||||
@ -244,7 +244,7 @@ const ChannelsTable = () => {
|
|||||||
const res = await API.get(`/api/channel/test?scope=${scope}`);
|
const res = await API.get(`/api/channel/test?scope=${scope}`);
|
||||||
const { success, message } = res.data;
|
const { success, message } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
showInfo('已成功开始测试通道,请刷新页面查看结果。');
|
showInfo('已成功开始测试渠道,请刷新页面查看结果。');
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
}
|
}
|
||||||
@ -270,7 +270,7 @@ const ChannelsTable = () => {
|
|||||||
newChannels[realIdx].balance = balance;
|
newChannels[realIdx].balance = balance;
|
||||||
newChannels[realIdx].balance_updated_time = Date.now() / 1000;
|
newChannels[realIdx].balance_updated_time = Date.now() / 1000;
|
||||||
setChannels(newChannels);
|
setChannels(newChannels);
|
||||||
showInfo(`通道 ${name} 余额更新成功!`);
|
showInfo(`渠道 ${name} 余额更新成功!`);
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
}
|
}
|
||||||
@ -281,7 +281,7 @@ const ChannelsTable = () => {
|
|||||||
const res = await API.get(`/api/channel/update_balance`);
|
const res = await API.get(`/api/channel/update_balance`);
|
||||||
const { success, message } = res.data;
|
const { success, message } = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
showInfo('已更新完毕所有已启用通道余额!');
|
showInfo('已更新完毕所有已启用渠道余额!');
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
}
|
}
|
||||||
|
@ -261,7 +261,7 @@ const OperationSetting = () => {
|
|||||||
value={inputs.ChannelDisableThreshold}
|
value={inputs.ChannelDisableThreshold}
|
||||||
type='number'
|
type='number'
|
||||||
min='0'
|
min='0'
|
||||||
placeholder='单位秒,当运行通道全部测试时,超过此时间将自动禁用通道'
|
placeholder='单位秒,当运行渠道全部测试时,超过此时间将自动禁用渠道'
|
||||||
/>
|
/>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='额度提醒阈值'
|
label='额度提醒阈值'
|
||||||
@ -277,13 +277,13 @@ const OperationSetting = () => {
|
|||||||
<Form.Group inline>
|
<Form.Group inline>
|
||||||
<Form.Checkbox
|
<Form.Checkbox
|
||||||
checked={inputs.AutomaticDisableChannelEnabled === 'true'}
|
checked={inputs.AutomaticDisableChannelEnabled === 'true'}
|
||||||
label='失败时自动禁用通道'
|
label='失败时自动禁用渠道'
|
||||||
name='AutomaticDisableChannelEnabled'
|
name='AutomaticDisableChannelEnabled'
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
<Form.Checkbox
|
<Form.Checkbox
|
||||||
checked={inputs.AutomaticEnableChannelEnabled === 'true'}
|
checked={inputs.AutomaticEnableChannelEnabled === 'true'}
|
||||||
label='成功时自动启用通道'
|
label='成功时自动启用渠道'
|
||||||
name='AutomaticEnableChannelEnabled'
|
name='AutomaticEnableChannelEnabled'
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
|
@ -83,6 +83,7 @@ const EditChannel = () => {
|
|||||||
data.model_mapping = JSON.stringify(JSON.parse(data.model_mapping), null, 2);
|
data.model_mapping = JSON.stringify(JSON.parse(data.model_mapping), null, 2);
|
||||||
}
|
}
|
||||||
setInputs(data);
|
setInputs(data);
|
||||||
|
setBasicModels(getChannelModels(data.type));
|
||||||
} else {
|
} else {
|
||||||
showError(message);
|
showError(message);
|
||||||
}
|
}
|
||||||
@ -99,9 +100,6 @@ const EditChannel = () => {
|
|||||||
}));
|
}));
|
||||||
setOriginModelOptions(localModelOptions);
|
setOriginModelOptions(localModelOptions);
|
||||||
setFullModels(res.data.data.map((model) => model.id));
|
setFullModels(res.data.data.map((model) => model.id));
|
||||||
setBasicModels(res.data.data.filter((model) => {
|
|
||||||
return model.id.startsWith('gpt-3') || model.id.startsWith('text-');
|
|
||||||
}).map((model) => model.id));
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showError(error.message);
|
showError(error.message);
|
||||||
}
|
}
|
||||||
@ -137,6 +135,9 @@ const EditChannel = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isEdit) {
|
if (isEdit) {
|
||||||
loadChannel().then();
|
loadChannel().then();
|
||||||
|
} else {
|
||||||
|
let localModels = getChannelModels(inputs.type);
|
||||||
|
setBasicModels(localModels);
|
||||||
}
|
}
|
||||||
fetchModels().then();
|
fetchModels().then();
|
||||||
fetchGroups().then();
|
fetchGroups().then();
|
||||||
@ -355,7 +356,7 @@ const EditChannel = () => {
|
|||||||
<div style={{ lineHeight: '40px', marginBottom: '12px' }}>
|
<div style={{ lineHeight: '40px', marginBottom: '12px' }}>
|
||||||
<Button type={'button'} onClick={() => {
|
<Button type={'button'} onClick={() => {
|
||||||
handleInputChange(null, { name: 'models', value: basicModels });
|
handleInputChange(null, { name: 'models', value: basicModels });
|
||||||
}}>填入基础模型</Button>
|
}}>填入相关模型</Button>
|
||||||
<Button type={'button'} onClick={() => {
|
<Button type={'button'} onClick={() => {
|
||||||
handleInputChange(null, { name: 'models', value: fullModels });
|
handleInputChange(null, { name: 'models', value: fullModels });
|
||||||
}}>填入所有模型</Button>
|
}}>填入所有模型</Button>
|
||||||
|
Loading…
Reference in New Issue
Block a user