mirror of
				https://github.com/songquanpeng/one-api.git
				synced 2025-10-31 13:53:41 +08:00 
			
		
		
		
	Compare commits
	
		
			5 Commits
		
	
	
		
			v0.4.0-alp
			...
			v0.4.1-alp
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 145bb14cb2 | ||
|  | 8901f03864 | ||
|  | 813bf0bd66 | ||
|  | 45e9fd66e7 | ||
|  | e0d0674f81 | 
| @@ -46,9 +46,11 @@ _✨ All in one 的 OpenAI 接口,整合各种 API 访问方式,开箱即用 | ||||
|  | ||||
| > **Warning**:使用 Docker 拉取的最新镜像可能是 `alpha` 版本,如果追求稳定性请手动指定版本。 | ||||
|  | ||||
| > **Warning**:从 `v0.3` 版本升级到 `v0.4` 版本需要手动迁移数据库,请手动执行[数据库迁移脚本](./bin/migration_v0.3-v0.4.sql)。 | ||||
|  | ||||
| ## 功能 | ||||
| 1. 支持多种 API 访问渠道,欢迎 PR 或提 issue 添加更多渠道: | ||||
|    + [x] OpenAI 官方通道 | ||||
|    + [x] OpenAI 官方通道(支持配置代理) | ||||
|    + [x] **Azure OpenAI API** | ||||
|    + [x] [API2D](https://api2d.com/r/197971) | ||||
|    + [x] [OhMyGPT](https://aigptx.top?aff=uFpUl2Kf) | ||||
| @@ -57,7 +59,7 @@ _✨ All in one 的 OpenAI 接口,整合各种 API 访问方式,开箱即用 | ||||
|    + [x] [OpenAI Max](https://openaimax.com) | ||||
|    + [x] [OpenAI-SB](https://openai-sb.com) | ||||
|    + [x] [CloseAI](https://console.openai-asia.com/r/2412) | ||||
|    + [x] 自定义渠道:例如使用自行搭建的 OpenAI 代理 | ||||
|    + [x] 自定义渠道:例如各种未收录的第三方代理服务 | ||||
| 2. 支持通过**负载均衡**的方式访问多个渠道。 | ||||
| 3. 支持 **stream 模式**,可以通过流式传输实现打字机效果。 | ||||
| 4. 支持**多机部署**,[详见此处](#多机部署)。 | ||||
|   | ||||
							
								
								
									
										17
									
								
								bin/migration_v0.3-v0.4.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								bin/migration_v0.3-v0.4.sql
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| INSERT INTO abilities (`group`, model, channel_id, enabled) | ||||
| SELECT c.`group`, m.model, c.id, 1 | ||||
| FROM channels c | ||||
| CROSS JOIN ( | ||||
|     SELECT 'gpt-3.5-turbo' AS model UNION ALL | ||||
|     SELECT 'gpt-3.5-turbo-0301' AS model UNION ALL | ||||
|     SELECT 'gpt-4' AS model UNION ALL | ||||
|     SELECT 'gpt-4-0314' AS model | ||||
| ) AS m | ||||
| WHERE c.status = 1 | ||||
|   AND NOT EXISTS ( | ||||
|     SELECT 1 | ||||
|     FROM abilities a | ||||
|     WHERE a.`group` = c.`group` | ||||
|       AND a.model = m.model | ||||
|       AND a.channel_id = c.id | ||||
| ); | ||||
| @@ -25,6 +25,7 @@ var OptionMap map[string]string | ||||
| var OptionMapRWMutex sync.RWMutex | ||||
|  | ||||
| var ItemsPerPage = 10 | ||||
| var MaxRecentItems = 100 | ||||
|  | ||||
| var PasswordLoginEnabled = true | ||||
| var PasswordRegisterEnabled = true | ||||
|   | ||||
| @@ -41,7 +41,9 @@ func updateChannelBalance(channel *model.Channel) (float64, error) { | ||||
| 	baseURL := common.ChannelBaseURLs[channel.Type] | ||||
| 	switch channel.Type { | ||||
| 	case common.ChannelTypeOpenAI: | ||||
| 		// do nothing | ||||
| 		if channel.BaseURL != "" { | ||||
| 			baseURL = channel.BaseURL | ||||
| 		} | ||||
| 	case common.ChannelTypeAzure: | ||||
| 		return 0, errors.New("尚未实现") | ||||
| 	case common.ChannelTypeCustom: | ||||
|   | ||||
| @@ -27,6 +27,8 @@ func testChannel(channel *model.Channel, request *ChatRequest) error { | ||||
| 	} else { | ||||
| 		if channel.Type == common.ChannelTypeCustom { | ||||
| 			requestURL = channel.BaseURL | ||||
| 		} else if channel.Type == common.ChannelTypeOpenAI && channel.BaseURL != "" { | ||||
| 			requestURL = channel.BaseURL | ||||
| 		} | ||||
| 		requestURL += "/v1/chat/completions" | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										86
									
								
								controller/log.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								controller/log.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | ||||
| package controller | ||||
|  | ||||
| import ( | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"one-api/common" | ||||
| 	"one-api/model" | ||||
| 	"strconv" | ||||
| ) | ||||
|  | ||||
| func GetAllLogs(c *gin.Context) { | ||||
| 	p, _ := strconv.Atoi(c.Query("p")) | ||||
| 	if p < 0 { | ||||
| 		p = 0 | ||||
| 	} | ||||
| 	logType, _ := strconv.Atoi(c.Query("type")) | ||||
| 	logs, err := model.GetAllLogs(logType, p*common.ItemsPerPage, common.ItemsPerPage) | ||||
| 	if err != nil { | ||||
| 		c.JSON(200, gin.H{ | ||||
| 			"success": false, | ||||
| 			"message": err.Error(), | ||||
| 		}) | ||||
| 		return | ||||
| 	} | ||||
| 	c.JSON(200, gin.H{ | ||||
| 		"success": true, | ||||
| 		"message": "", | ||||
| 		"data":    logs, | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func GetUserLogs(c *gin.Context) { | ||||
| 	p, _ := strconv.Atoi(c.Query("p")) | ||||
| 	if p < 0 { | ||||
| 		p = 0 | ||||
| 	} | ||||
| 	userId := c.GetInt("id") | ||||
| 	logType, _ := strconv.Atoi(c.Query("type")) | ||||
| 	logs, err := model.GetUserLogs(userId, logType, p*common.ItemsPerPage, common.ItemsPerPage) | ||||
| 	if err != nil { | ||||
| 		c.JSON(200, gin.H{ | ||||
| 			"success": false, | ||||
| 			"message": err.Error(), | ||||
| 		}) | ||||
| 		return | ||||
| 	} | ||||
| 	c.JSON(200, gin.H{ | ||||
| 		"success": true, | ||||
| 		"message": "", | ||||
| 		"data":    logs, | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func SearchAllLogs(c *gin.Context) { | ||||
| 	keyword := c.Query("keyword") | ||||
| 	logs, err := model.SearchAllLogs(keyword) | ||||
| 	if err != nil { | ||||
| 		c.JSON(200, gin.H{ | ||||
| 			"success": false, | ||||
| 			"message": err.Error(), | ||||
| 		}) | ||||
| 		return | ||||
| 	} | ||||
| 	c.JSON(200, gin.H{ | ||||
| 		"success": true, | ||||
| 		"message": "", | ||||
| 		"data":    logs, | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func SearchUserLogs(c *gin.Context) { | ||||
| 	keyword := c.Query("keyword") | ||||
| 	userId := c.GetInt("id") | ||||
| 	logs, err := model.SearchUserLogs(userId, keyword) | ||||
| 	if err != nil { | ||||
| 		c.JSON(200, gin.H{ | ||||
| 			"success": false, | ||||
| 			"message": err.Error(), | ||||
| 		}) | ||||
| 		return | ||||
| 	} | ||||
| 	c.JSON(200, gin.H{ | ||||
| 		"success": true, | ||||
| 		"message": "", | ||||
| 		"data":    logs, | ||||
| 	}) | ||||
| } | ||||
| @@ -147,6 +147,10 @@ func relayHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode { | ||||
| 	requestURL := c.Request.URL.String() | ||||
| 	if channelType == common.ChannelTypeCustom { | ||||
| 		baseURL = c.GetString("base_url") | ||||
| 	} else if channelType == common.ChannelTypeOpenAI { | ||||
| 		if c.GetString("base_url") != "" { | ||||
| 			baseURL = c.GetString("base_url") | ||||
| 		} | ||||
| 	} | ||||
| 	fullRequestURL := fmt.Sprintf("%s%s", baseURL, requestURL) | ||||
| 	if channelType == common.ChannelTypeAzure { | ||||
|   | ||||
| @@ -82,11 +82,9 @@ func Distribute() func(c *gin.Context) { | ||||
| 		c.Set("channel_id", channel.Id) | ||||
| 		c.Set("channel_name", channel.Name) | ||||
| 		c.Request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", channel.Key)) | ||||
| 		if channel.Type == common.ChannelTypeCustom || channel.Type == common.ChannelTypeAzure { | ||||
| 			c.Set("base_url", channel.BaseURL) | ||||
| 			if channel.Type == common.ChannelTypeAzure { | ||||
| 				c.Set("api_version", channel.Other) | ||||
| 			} | ||||
| 		c.Set("base_url", channel.BaseURL) | ||||
| 		if channel.Type == common.ChannelTypeAzure { | ||||
| 			c.Set("api_version", channel.Other) | ||||
| 		} | ||||
| 		c.Next() | ||||
| 	} | ||||
|   | ||||
| @@ -13,9 +13,6 @@ type Ability struct { | ||||
| } | ||||
|  | ||||
| func GetRandomSatisfiedChannel(group string, model string) (*Channel, error) { | ||||
| 	if group == "default" { | ||||
| 		return GetRandomChannel() | ||||
| 	} | ||||
| 	ability := Ability{} | ||||
| 	var err error = nil | ||||
| 	if common.UsingSQLite { | ||||
|   | ||||
							
								
								
									
										44
									
								
								model/log.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								model/log.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| package model | ||||
|  | ||||
| import "one-api/common" | ||||
|  | ||||
| type Log struct { | ||||
| 	Id        int    `json:"id"` | ||||
| 	UserId    int    `json:"user_id" gorm:"index"` | ||||
| 	CreatedAt int64  `json:"created_at" gorm:"bigint"` | ||||
| 	Type      int    `json:"type" gorm:"index"` | ||||
| 	Content   string `json:"content"` | ||||
| } | ||||
|  | ||||
| func RecordLog(userId int, logType int, content string) { | ||||
| 	log := &Log{ | ||||
| 		UserId:    userId, | ||||
| 		CreatedAt: common.GetTimestamp(), | ||||
| 		Type:      logType, | ||||
| 		Content:   content, | ||||
| 	} | ||||
| 	err := DB.Create(log).Error | ||||
| 	if err != nil { | ||||
| 		common.SysError("failed to record log: " + err.Error()) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func GetAllLogs(logType int, startIdx int, num int) (logs []*Log, err error) { | ||||
| 	err = DB.Where("type = ?", logType).Order("id desc").Limit(num).Offset(startIdx).Find(&logs).Error | ||||
| 	return logs, err | ||||
| } | ||||
|  | ||||
| func GetUserLogs(userId int, logType int, startIdx int, num int) (logs []*Log, err error) { | ||||
| 	err = DB.Where("user_id = ? and type = ?", userId, logType).Order("id desc").Limit(num).Offset(startIdx).Find(&logs).Error | ||||
| 	return logs, err | ||||
| } | ||||
|  | ||||
| func SearchAllLogs(keyword string) (logs []*Log, err error) { | ||||
| 	err = DB.Where("type = ? or content LIKE ?", keyword, keyword+"%").Order("id desc").Limit(common.MaxRecentItems).Find(&logs).Error | ||||
| 	return logs, err | ||||
| } | ||||
|  | ||||
| func SearchUserLogs(userId int, keyword string) (logs []*Log, err error) { | ||||
| 	err = DB.Where("user_id = ? and type = ?", userId, keyword).Order("id desc").Limit(common.MaxRecentItems).Find(&logs).Error | ||||
| 	return logs, err | ||||
| } | ||||
| @@ -83,7 +83,7 @@ func (redemption *Redemption) SelectUpdate() error { | ||||
| // Update Make sure your token's fields is completed, because this will update non-zero values | ||||
| func (redemption *Redemption) Update() error { | ||||
| 	var err error | ||||
| 	err = DB.Model(redemption).Select("name", "status", "redeemed_time").Updates(redemption).Error | ||||
| 	err = DB.Model(redemption).Select("name", "status", "quota", "redeemed_time").Updates(redemption).Error | ||||
| 	return err | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -93,5 +93,10 @@ func SetApiRouter(router *gin.Engine) { | ||||
| 			redemptionRoute.PUT("/", controller.UpdateRedemption) | ||||
| 			redemptionRoute.DELETE("/:id", controller.DeleteRedemption) | ||||
| 		} | ||||
| 		logRoute := apiRouter.Group("/log") | ||||
| 		logRoute.GET("/", middleware.AdminAuth(), controller.GetAllLogs) | ||||
| 		logRoute.GET("/search", middleware.AdminAuth(), controller.SearchAllLogs) | ||||
| 		logRoute.GET("/self", middleware.UserAuth(), controller.GetUserLogs) | ||||
| 		logRoute.GET("/self/search", middleware.UserAuth(), controller.SearchUserLogs) | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -198,6 +198,20 @@ const EditChannel = () => { | ||||
|               handleInputChange(null, { name: 'models', value: fullModels }); | ||||
|             }}>填入所有模型</Button> | ||||
|           </div> | ||||
|           { | ||||
|             inputs.type === 1 && ( | ||||
|               <Form.Field> | ||||
|                 <Form.Input | ||||
|                   label='代理' | ||||
|                   name='base_url' | ||||
|                   placeholder={'请输入 OpenAI API 代理地址,如果不需要请留空,格式为:https://api.openai.com'} | ||||
|                   onChange={handleInputChange} | ||||
|                   value={inputs.base_url} | ||||
|                   autoComplete='new-password' | ||||
|                 /> | ||||
|               </Form.Field> | ||||
|             ) | ||||
|           } | ||||
|           { | ||||
|             batch ? <Form.Field> | ||||
|               <Form.TextArea | ||||
|   | ||||
		Reference in New Issue
	
	Block a user