mirror of
				https://github.com/songquanpeng/one-api.git
				synced 2025-10-31 13:53:41 +08:00 
			
		
		
		
	Compare commits
	
		
			5 Commits
		
	
	
		
			v0.6.4-alp
			...
			v0.6.5-alp
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 2ba28c72cb | ||
|  | 5e81e19bc8 | ||
|  | 96d7a99312 | ||
|  | 24be9de098 | ||
|  | 5b349efff9 | 
| @@ -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 大语言模型的知识库问答系统 | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								common/conv/any.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								common/conv/any.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | package conv | ||||||
|  |  | ||||||
|  | func AsString(v any) string { | ||||||
|  | 	str, _ := v.(string) | ||||||
|  | 	return str | ||||||
|  | } | ||||||
| @@ -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())) | ||||||
| 			} | 			} | ||||||
|   | |||||||
							
								
								
									
										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", | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ package model | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"github.com/songquanpeng/one-api/common" | 	"github.com/songquanpeng/one-api/common" | ||||||
|  | 	"gorm.io/gorm" | ||||||
| 	"strings" | 	"strings" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -13,7 +14,7 @@ type Ability struct { | |||||||
| 	Priority  *int64 `json:"priority" gorm:"bigint;default:0;index"` | 	Priority  *int64 `json:"priority" gorm:"bigint;default:0;index"` | ||||||
| } | } | ||||||
|  |  | ||||||
| func GetRandomSatisfiedChannel(group string, model string) (*Channel, error) { | func GetRandomSatisfiedChannel(group string, model string, ignoreFirstPriority bool) (*Channel, error) { | ||||||
| 	ability := Ability{} | 	ability := Ability{} | ||||||
| 	groupCol := "`group`" | 	groupCol := "`group`" | ||||||
| 	trueVal := "1" | 	trueVal := "1" | ||||||
| @@ -23,8 +24,13 @@ func GetRandomSatisfiedChannel(group string, model string) (*Channel, error) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var err error = nil | 	var err error = nil | ||||||
| 	maxPrioritySubQuery := DB.Model(&Ability{}).Select("MAX(priority)").Where(groupCol+" = ? and model = ? and enabled = "+trueVal, group, model) | 	var channelQuery *gorm.DB | ||||||
| 	channelQuery := DB.Where(groupCol+" = ? and model = ? and enabled = "+trueVal+" and priority = (?)", group, model, maxPrioritySubQuery) | 	if ignoreFirstPriority { | ||||||
|  | 		channelQuery = DB.Where(groupCol+" = ? and model = ? and enabled = "+trueVal, group, model) | ||||||
|  | 	} else { | ||||||
|  | 		maxPrioritySubQuery := DB.Model(&Ability{}).Select("MAX(priority)").Where(groupCol+" = ? and model = ? and enabled = "+trueVal, group, model) | ||||||
|  | 		channelQuery = DB.Where(groupCol+" = ? and model = ? and enabled = "+trueVal+" and priority = (?)", group, model, maxPrioritySubQuery) | ||||||
|  | 	} | ||||||
| 	if common.UsingSQLite || common.UsingPostgreSQL { | 	if common.UsingSQLite || common.UsingPostgreSQL { | ||||||
| 		err = channelQuery.Order("RANDOM()").First(&ability).Error | 		err = channelQuery.Order("RANDOM()").First(&ability).Error | ||||||
| 	} else { | 	} else { | ||||||
|   | |||||||
| @@ -205,7 +205,7 @@ func SyncChannelCache(frequency int) { | |||||||
|  |  | ||||||
| func CacheGetRandomSatisfiedChannel(group string, model string, ignoreFirstPriority bool) (*Channel, error) { | func CacheGetRandomSatisfiedChannel(group string, model string, ignoreFirstPriority bool) (*Channel, error) { | ||||||
| 	if !config.MemoryCacheEnabled { | 	if !config.MemoryCacheEnabled { | ||||||
| 		return GetRandomSatisfiedChannel(group, model) | 		return GetRandomSatisfiedChannel(group, model, ignoreFirstPriority) | ||||||
| 	} | 	} | ||||||
| 	channelSyncLock.RLock() | 	channelSyncLock.RLock() | ||||||
| 	defer channelSyncLock.RUnlock() | 	defer channelSyncLock.RUnlock() | ||||||
|   | |||||||
| @@ -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) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -48,7 +48,10 @@ func ConvertRequest(request model.GeneralOpenAIRequest) *ChatRequest { | |||||||
| 			MaxTokens:         request.MaxTokens, | 			MaxTokens:         request.MaxTokens, | ||||||
| 			Temperature:       request.Temperature, | 			Temperature:       request.Temperature, | ||||||
| 			TopP:              request.TopP, | 			TopP:              request.TopP, | ||||||
|  | 			TopK:              request.TopK, | ||||||
|  | 			ResultFormat:      "message", | ||||||
| 		}, | 		}, | ||||||
|  | 		Tools: request.Tools, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -117,19 +120,11 @@ func embeddingResponseAli2OpenAI(response *EmbeddingResponse) *openai.EmbeddingR | |||||||
| } | } | ||||||
|  |  | ||||||
| func responseAli2OpenAI(response *ChatResponse) *openai.TextResponse { | func responseAli2OpenAI(response *ChatResponse) *openai.TextResponse { | ||||||
| 	choice := openai.TextResponseChoice{ |  | ||||||
| 		Index: 0, |  | ||||||
| 		Message: model.Message{ |  | ||||||
| 			Role:    "assistant", |  | ||||||
| 			Content: response.Output.Text, |  | ||||||
| 		}, |  | ||||||
| 		FinishReason: response.Output.FinishReason, |  | ||||||
| 	} |  | ||||||
| 	fullTextResponse := openai.TextResponse{ | 	fullTextResponse := openai.TextResponse{ | ||||||
| 		Id:      response.RequestId, | 		Id:      response.RequestId, | ||||||
| 		Object:  "chat.completion", | 		Object:  "chat.completion", | ||||||
| 		Created: helper.GetTimestamp(), | 		Created: helper.GetTimestamp(), | ||||||
| 		Choices: []openai.TextResponseChoice{choice}, | 		Choices: response.Output.Choices, | ||||||
| 		Usage: model.Usage{ | 		Usage: model.Usage{ | ||||||
| 			PromptTokens:     response.Usage.InputTokens, | 			PromptTokens:     response.Usage.InputTokens, | ||||||
| 			CompletionTokens: response.Usage.OutputTokens, | 			CompletionTokens: response.Usage.OutputTokens, | ||||||
| @@ -140,10 +135,14 @@ func responseAli2OpenAI(response *ChatResponse) *openai.TextResponse { | |||||||
| } | } | ||||||
|  |  | ||||||
| func streamResponseAli2OpenAI(aliResponse *ChatResponse) *openai.ChatCompletionsStreamResponse { | func streamResponseAli2OpenAI(aliResponse *ChatResponse) *openai.ChatCompletionsStreamResponse { | ||||||
|  | 	if len(aliResponse.Output.Choices) == 0 { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	aliChoice := aliResponse.Output.Choices[0] | ||||||
| 	var choice openai.ChatCompletionsStreamResponseChoice | 	var choice openai.ChatCompletionsStreamResponseChoice | ||||||
| 	choice.Delta.Content = aliResponse.Output.Text | 	choice.Delta = aliChoice.Message | ||||||
| 	if aliResponse.Output.FinishReason != "null" { | 	if aliChoice.FinishReason != "null" { | ||||||
| 		finishReason := aliResponse.Output.FinishReason | 		finishReason := aliChoice.FinishReason | ||||||
| 		choice.FinishReason = &finishReason | 		choice.FinishReason = &finishReason | ||||||
| 	} | 	} | ||||||
| 	response := openai.ChatCompletionsStreamResponse{ | 	response := openai.ChatCompletionsStreamResponse{ | ||||||
| @@ -204,6 +203,9 @@ func StreamHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusC | |||||||
| 				usage.TotalTokens = aliResponse.Usage.InputTokens + aliResponse.Usage.OutputTokens | 				usage.TotalTokens = aliResponse.Usage.InputTokens + aliResponse.Usage.OutputTokens | ||||||
| 			} | 			} | ||||||
| 			response := streamResponseAli2OpenAI(&aliResponse) | 			response := streamResponseAli2OpenAI(&aliResponse) | ||||||
|  | 			if response == nil { | ||||||
|  | 				return true | ||||||
|  | 			} | ||||||
| 			//response.Choices[0].Delta.Content = strings.TrimPrefix(response.Choices[0].Delta.Content, lastResponseText) | 			//response.Choices[0].Delta.Content = strings.TrimPrefix(response.Choices[0].Delta.Content, lastResponseText) | ||||||
| 			//lastResponseText = aliResponse.Output.Text | 			//lastResponseText = aliResponse.Output.Text | ||||||
| 			jsonResponse, err := json.Marshal(response) | 			jsonResponse, err := json.Marshal(response) | ||||||
| @@ -226,6 +228,7 @@ func StreamHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusC | |||||||
| } | } | ||||||
|  |  | ||||||
| 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 := c.Request.Context() | ||||||
| 	var aliResponse ChatResponse | 	var aliResponse ChatResponse | ||||||
| 	responseBody, err := io.ReadAll(resp.Body) | 	responseBody, err := io.ReadAll(resp.Body) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -235,6 +238,7 @@ func Handler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusCode, * | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil | 		return openai.ErrorWrapper(err, "close_response_body_failed", http.StatusInternalServerError), nil | ||||||
| 	} | 	} | ||||||
|  | 	logger.Debugf(ctx, "response body: %s\n", responseBody) | ||||||
| 	err = json.Unmarshal(responseBody, &aliResponse) | 	err = json.Unmarshal(responseBody, &aliResponse) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return openai.ErrorWrapper(err, "unmarshal_response_body_failed", http.StatusInternalServerError), nil | 		return openai.ErrorWrapper(err, "unmarshal_response_body_failed", http.StatusInternalServerError), nil | ||||||
|   | |||||||
| @@ -1,5 +1,10 @@ | |||||||
| package ali | package ali | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"github.com/songquanpeng/one-api/relay/channel/openai" | ||||||
|  | 	"github.com/songquanpeng/one-api/relay/model" | ||||||
|  | ) | ||||||
|  |  | ||||||
| type Message struct { | type Message struct { | ||||||
| 	Content string `json:"content"` | 	Content string `json:"content"` | ||||||
| 	Role    string `json:"role"` | 	Role    string `json:"role"` | ||||||
| @@ -18,12 +23,14 @@ type Parameters struct { | |||||||
| 	IncrementalOutput bool    `json:"incremental_output,omitempty"` | 	IncrementalOutput bool    `json:"incremental_output,omitempty"` | ||||||
| 	MaxTokens         int     `json:"max_tokens,omitempty"` | 	MaxTokens         int     `json:"max_tokens,omitempty"` | ||||||
| 	Temperature       float64 `json:"temperature,omitempty"` | 	Temperature       float64 `json:"temperature,omitempty"` | ||||||
|  | 	ResultFormat      string  `json:"result_format,omitempty"` | ||||||
| } | } | ||||||
|  |  | ||||||
| type ChatRequest struct { | type ChatRequest struct { | ||||||
| 	Model      string     `json:"model"` | 	Model      string       `json:"model"` | ||||||
| 	Input      Input      `json:"input"` | 	Input      Input        `json:"input"` | ||||||
| 	Parameters Parameters `json:"parameters,omitempty"` | 	Parameters Parameters   `json:"parameters,omitempty"` | ||||||
|  | 	Tools      []model.Tool `json:"tools,omitempty"` | ||||||
| } | } | ||||||
|  |  | ||||||
| type EmbeddingRequest struct { | type EmbeddingRequest struct { | ||||||
| @@ -62,8 +69,9 @@ type Usage struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| type Output struct { | type Output struct { | ||||||
| 	Text         string `json:"text"` | 	//Text         string                      `json:"text"` | ||||||
| 	FinishReason string `json:"finish_reason"` | 	//FinishReason string                      `json:"finish_reason"` | ||||||
|  | 	Choices []openai.TextResponseChoice `json:"choices"` | ||||||
| } | } | ||||||
|  |  | ||||||
| type ChatResponse struct { | type ChatResponse struct { | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ import ( | |||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"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/conv" | ||||||
| 	"github.com/songquanpeng/one-api/common/logger" | 	"github.com/songquanpeng/one-api/common/logger" | ||||||
| 	"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" | ||||||
| @@ -53,7 +54,7 @@ func StreamHandler(c *gin.Context, resp *http.Response, relayMode int) (*model.E | |||||||
| 						continue // just ignore the error | 						continue // just ignore the error | ||||||
| 					} | 					} | ||||||
| 					for _, choice := range streamResponse.Choices { | 					for _, choice := range streamResponse.Choices { | ||||||
| 						responseText += choice.Delta.Content | 						responseText += conv.AsString(choice.Delta.Content) | ||||||
| 					} | 					} | ||||||
| 					if streamResponse.Usage != nil { | 					if streamResponse.Usage != nil { | ||||||
| 						usage = streamResponse.Usage | 						usage = streamResponse.Usage | ||||||
|   | |||||||
| @@ -118,12 +118,9 @@ type ImageResponse struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| type ChatCompletionsStreamResponseChoice struct { | type ChatCompletionsStreamResponseChoice struct { | ||||||
| 	Index int `json:"index"` | 	Index        int           `json:"index"` | ||||||
| 	Delta struct { | 	Delta        model.Message `json:"delta"` | ||||||
| 		Content string `json:"content"` | 	FinishReason *string       `json:"finish_reason,omitempty"` | ||||||
| 		Role    string `json:"role,omitempty"` |  | ||||||
| 	} `json:"delta"` |  | ||||||
| 	FinishReason *string `json:"finish_reason,omitempty"` |  | ||||||
| } | } | ||||||
|  |  | ||||||
| type ChatCompletionsStreamResponse struct { | type ChatCompletionsStreamResponse struct { | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"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/conv" | ||||||
| 	"github.com/songquanpeng/one-api/common/helper" | 	"github.com/songquanpeng/one-api/common/helper" | ||||||
| 	"github.com/songquanpeng/one-api/common/logger" | 	"github.com/songquanpeng/one-api/common/logger" | ||||||
| 	"github.com/songquanpeng/one-api/relay/channel/openai" | 	"github.com/songquanpeng/one-api/relay/channel/openai" | ||||||
| @@ -129,7 +130,7 @@ func StreamHandler(c *gin.Context, resp *http.Response) (*model.ErrorWithStatusC | |||||||
| 			} | 			} | ||||||
| 			response := streamResponseTencent2OpenAI(&TencentResponse) | 			response := streamResponseTencent2OpenAI(&TencentResponse) | ||||||
| 			if len(response.Choices) != 0 { | 			if len(response.Choices) != 0 { | ||||||
| 				responseText += response.Choices[0].Delta.Content | 				responseText += conv.AsString(response.Choices[0].Delta.Content) | ||||||
| 			} | 			} | ||||||
| 			jsonResponse, err := json.Marshal(response) | 			jsonResponse, err := json.Marshal(response) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
|   | |||||||
| @@ -5,25 +5,27 @@ type ResponseFormat struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| type GeneralOpenAIRequest struct { | type GeneralOpenAIRequest struct { | ||||||
| 	Model            string          `json:"model,omitempty"` |  | ||||||
| 	Messages         []Message       `json:"messages,omitempty"` | 	Messages         []Message       `json:"messages,omitempty"` | ||||||
| 	Prompt           any             `json:"prompt,omitempty"` | 	Model            string          `json:"model,omitempty"` | ||||||
| 	Stream           bool            `json:"stream,omitempty"` |  | ||||||
| 	MaxTokens        int             `json:"max_tokens,omitempty"` |  | ||||||
| 	Temperature      float64         `json:"temperature,omitempty"` |  | ||||||
| 	TopP             float64         `json:"top_p,omitempty"` |  | ||||||
| 	N                int             `json:"n,omitempty"` |  | ||||||
| 	Input            any             `json:"input,omitempty"` |  | ||||||
| 	Instruction      string          `json:"instruction,omitempty"` |  | ||||||
| 	Size             string          `json:"size,omitempty"` |  | ||||||
| 	Functions        any             `json:"functions,omitempty"` |  | ||||||
| 	FrequencyPenalty float64         `json:"frequency_penalty,omitempty"` | 	FrequencyPenalty float64         `json:"frequency_penalty,omitempty"` | ||||||
|  | 	MaxTokens        int             `json:"max_tokens,omitempty"` | ||||||
|  | 	N                int             `json:"n,omitempty"` | ||||||
| 	PresencePenalty  float64         `json:"presence_penalty,omitempty"` | 	PresencePenalty  float64         `json:"presence_penalty,omitempty"` | ||||||
| 	ResponseFormat   *ResponseFormat `json:"response_format,omitempty"` | 	ResponseFormat   *ResponseFormat `json:"response_format,omitempty"` | ||||||
| 	Seed             float64         `json:"seed,omitempty"` | 	Seed             float64         `json:"seed,omitempty"` | ||||||
| 	Tools            any             `json:"tools,omitempty"` | 	Stream           bool            `json:"stream,omitempty"` | ||||||
|  | 	Temperature      float64         `json:"temperature,omitempty"` | ||||||
|  | 	TopP             float64         `json:"top_p,omitempty"` | ||||||
|  | 	TopK             int             `json:"top_k,omitempty"` | ||||||
|  | 	Tools            []Tool          `json:"tools,omitempty"` | ||||||
| 	ToolChoice       any             `json:"tool_choice,omitempty"` | 	ToolChoice       any             `json:"tool_choice,omitempty"` | ||||||
|  | 	FunctionCall     any             `json:"function_call,omitempty"` | ||||||
|  | 	Functions        any             `json:"functions,omitempty"` | ||||||
| 	User             string          `json:"user,omitempty"` | 	User             string          `json:"user,omitempty"` | ||||||
|  | 	Prompt           any             `json:"prompt,omitempty"` | ||||||
|  | 	Input            any             `json:"input,omitempty"` | ||||||
|  | 	Instruction      string          `json:"instruction,omitempty"` | ||||||
|  | 	Size             string          `json:"size,omitempty"` | ||||||
| } | } | ||||||
|  |  | ||||||
| func (r GeneralOpenAIRequest) ParseInput() []string { | func (r GeneralOpenAIRequest) ParseInput() []string { | ||||||
|   | |||||||
| @@ -1,9 +1,10 @@ | |||||||
| package model | package model | ||||||
|  |  | ||||||
| type Message struct { | type Message struct { | ||||||
| 	Role    string  `json:"role"` | 	Role      string  `json:"role,omitempty"` | ||||||
| 	Content any     `json:"content"` | 	Content   any     `json:"content,omitempty"` | ||||||
| 	Name    *string `json:"name,omitempty"` | 	Name      *string `json:"name,omitempty"` | ||||||
|  | 	ToolCalls []Tool  `json:"tool_calls,omitempty"` | ||||||
| } | } | ||||||
|  |  | ||||||
| func (m Message) IsStringContent() bool { | func (m Message) IsStringContent() bool { | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								relay/model/tool.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								relay/model/tool.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | package model | ||||||
|  |  | ||||||
|  | type Tool struct { | ||||||
|  | 	Id       string   `json:"id,omitempty"` | ||||||
|  | 	Type     string   `json:"type"` | ||||||
|  | 	Function Function `json:"function"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type Function struct { | ||||||
|  | 	Description string `json:"description,omitempty"` | ||||||
|  | 	Name        string `json:"name"` | ||||||
|  | 	Parameters  any    `json:"parameters,omitempty"` // request | ||||||
|  | 	Arguments   any    `json:"arguments,omitempty"`  // response | ||||||
|  | } | ||||||
| @@ -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> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user