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