mirror of
				https://github.com/songquanpeng/one-api.git
				synced 2025-10-27 20:03:42 +08:00 
			
		
		
		
	Compare commits
	
		
			9 Commits
		
	
	
		
			v0.3.2
			...
			v0.3.3-alp
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | fa79e8b7a3 | ||
|  | 1cc7c20183 | ||
|  | 2eee97e9b6 | ||
|  | a3a1b612b0 | ||
|  | 61e682ca47 | ||
|  | b383983106 | ||
|  | cfd587117e | ||
|  | ef9dca28f5 | ||
|  | 741c0b9c18 | 
| @@ -51,16 +51,17 @@ _✨ All in one 的 OpenAI 接口,整合各种 API 访问方式,开箱即用 | |||||||
|    + [x] **Azure OpenAI API** |    + [x] **Azure OpenAI API** | ||||||
|    + [x] [API2D](https://api2d.com/r/197971) |    + [x] [API2D](https://api2d.com/r/197971) | ||||||
|    + [x] [OhMyGPT](https://aigptx.top?aff=uFpUl2Kf) |    + [x] [OhMyGPT](https://aigptx.top?aff=uFpUl2Kf) | ||||||
|  |    + [x] [AI Proxy](https://aiproxy.io/?i=OneAPI) (邀请码:`OneAPI`) | ||||||
|    + [x] [AI.LS](https://ai.ls) |    + [x] [AI.LS](https://ai.ls) | ||||||
|    + [x] [OpenAI Max](https://openaimax.com) |    + [x] [OpenAI Max](https://openaimax.com) | ||||||
|    + [x] [OpenAI-SB](https://openai-sb.com) |    + [x] [OpenAI-SB](https://openai-sb.com) | ||||||
|    + [x] [CloseAI](https://console.openai-asia.com) |    + [x] [CloseAI](https://console.openai-asia.com/r/2412) | ||||||
|    + [x] 自定义渠道:例如使用自行搭建的 OpenAI 代理 |    + [x] 自定义渠道:例如使用自行搭建的 OpenAI 代理 | ||||||
| 2. 支持通过**负载均衡**的方式访问多个渠道。 | 2. 支持通过**负载均衡**的方式访问多个渠道。 | ||||||
| 3. 支持 **stream 模式**,可以通过流式传输实现打字机效果。 | 3. 支持 **stream 模式**,可以通过流式传输实现打字机效果。 | ||||||
| 4. 支持**多机部署**,[详见此处](#多机部署)。 | 4. 支持**多机部署**,[详见此处](#多机部署)。 | ||||||
| 5. 支持**令牌管理**,设置令牌的过期时间和使用次数。 | 5. 支持**令牌管理**,设置令牌的过期时间和使用次数。 | ||||||
| 6. 支持**兑换码管理**,支持批量生成和导出兑换码,可使用兑换码为令牌进行充值。 | 6. 支持**兑换码管理**,支持批量生成和导出兑换码,可使用兑换码为账户进行充值。 | ||||||
| 7. 支持**通道管理**,批量创建通道。 | 7. 支持**通道管理**,批量创建通道。 | ||||||
| 8. 支持发布公告,设置充值链接,设置新用户初始额度。 | 8. 支持发布公告,设置充值链接,设置新用户初始额度。 | ||||||
| 9. 支持丰富的**自定义**设置, | 9. 支持丰富的**自定义**设置, | ||||||
|   | |||||||
| @@ -128,6 +128,7 @@ const ( | |||||||
| 	ChannelTypeOhMyGPT   = 7 | 	ChannelTypeOhMyGPT   = 7 | ||||||
| 	ChannelTypeCustom    = 8 | 	ChannelTypeCustom    = 8 | ||||||
| 	ChannelTypeAILS      = 9 | 	ChannelTypeAILS      = 9 | ||||||
|  | 	ChannelTypeAIProxy   = 10 | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var ChannelBaseURLs = []string{ | var ChannelBaseURLs = []string{ | ||||||
| @@ -141,4 +142,5 @@ var ChannelBaseURLs = []string{ | |||||||
| 	"https://api.ohmygpt.com",     // 7 | 	"https://api.ohmygpt.com",     // 7 | ||||||
| 	"",                            // 8 | 	"",                            // 8 | ||||||
| 	"https://api.caipacity.com",   // 9 | 	"https://api.caipacity.com",   // 9 | ||||||
|  | 	"https://api.aiproxy.io",      // 10 | ||||||
| } | } | ||||||
|   | |||||||
| @@ -15,7 +15,11 @@ func getTokenEncoder(model string) *tiktoken.Tiktoken { | |||||||
| 	} | 	} | ||||||
| 	tokenEncoder, err := tiktoken.EncodingForModel(model) | 	tokenEncoder, err := tiktoken.EncodingForModel(model) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		common.FatalLog(fmt.Sprintf("failed to get token encoder for model %s: %s", model, err.Error())) | 		common.SysError(fmt.Sprintf("failed to get token encoder for model %s: %s, using encoder for gpt-3.5-turbo", model, err.Error())) | ||||||
|  | 		tokenEncoder, err = tiktoken.EncodingForModel("gpt-3.5-turbo") | ||||||
|  | 		if err != nil { | ||||||
|  | 			common.FatalLog(fmt.Sprintf("failed to get token encoder for model gpt-3.5-turbo: %s", err.Error())) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	tokenEncoderMap[model] = tokenEncoder | 	tokenEncoderMap[model] = tokenEncoder | ||||||
| 	return tokenEncoder | 	return tokenEncoder | ||||||
|   | |||||||
| @@ -26,6 +26,7 @@ func createRootAccountIfNeed() error { | |||||||
| 			Status:      common.UserStatusEnabled, | 			Status:      common.UserStatusEnabled, | ||||||
| 			DisplayName: "Root User", | 			DisplayName: "Root User", | ||||||
| 			AccessToken: common.GetUUID(), | 			AccessToken: common.GetUUID(), | ||||||
|  | 			Quota:       100000000, | ||||||
| 		} | 		} | ||||||
| 		DB.Create(&rootUser) | 		DB.Create(&rootUser) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -19,8 +19,7 @@ type User struct { | |||||||
| 	Email            string `json:"email" gorm:"index" validate:"max=50"` | 	Email            string `json:"email" gorm:"index" validate:"max=50"` | ||||||
| 	GitHubId         string `json:"github_id" gorm:"column:github_id;index"` | 	GitHubId         string `json:"github_id" gorm:"column:github_id;index"` | ||||||
| 	WeChatId         string `json:"wechat_id" gorm:"column:wechat_id;index"` | 	WeChatId         string `json:"wechat_id" gorm:"column:wechat_id;index"` | ||||||
| 	VerificationCode string `json:"verification_code" gorm:"-:all"` // this field is only for Email verification, don't save it to database! | 	VerificationCode string `json:"verification_code" gorm:"-:all"`                                    // this field is only for Email verification, don't save it to database! | ||||||
| 	Balance          int    `json:"balance" gorm:"type:int;default:0"` |  | ||||||
| 	AccessToken      string `json:"access_token" gorm:"type:char(32);column:access_token;uniqueIndex"` // this token is for system management | 	AccessToken      string `json:"access_token" gorm:"type:char(32);column:access_token;uniqueIndex"` // this token is for system management | ||||||
| 	Quota            int    `json:"quota" gorm:"type:int;default:0"` | 	Quota            int    `json:"quota" gorm:"type:int;default:0"` | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,11 +1,12 @@ | |||||||
| export const CHANNEL_OPTIONS = [ | export const CHANNEL_OPTIONS = [ | ||||||
|   { key: 1, text: 'OpenAI', value: 1, color: 'green' }, |   { key: 1, text: 'OpenAI', value: 1, color: 'green' }, | ||||||
|   { key: 2, text: 'API2D', value: 2, color: 'blue' }, |   { key: 8, text: '自定义', value: 8, color: 'pink' }, | ||||||
|   { key: 3, text: 'Azure', value: 3, color: 'olive' }, |   { key: 3, text: 'Azure', value: 3, color: 'olive' }, | ||||||
|  |   { key: 2, text: 'API2D', value: 2, color: 'blue' }, | ||||||
|   { key: 4, text: 'CloseAI', value: 4, color: 'teal' }, |   { key: 4, text: 'CloseAI', value: 4, color: 'teal' }, | ||||||
|   { key: 5, text: 'OpenAI-SB', value: 5, color: 'brown' }, |   { key: 5, text: 'OpenAI-SB', value: 5, color: 'brown' }, | ||||||
|   { key: 6, text: 'OpenAI Max', value: 6, color: 'violet' }, |   { key: 6, text: 'OpenAI Max', value: 6, color: 'violet' }, | ||||||
|   { key: 7, text: 'OhMyGPT', value: 7, color: 'purple' }, |   { key: 7, text: 'OhMyGPT', value: 7, color: 'purple' }, | ||||||
|   { key: 9, text: 'AI.LS', value: 9, color: 'yellow' }, |   { key: 9, text: 'AI.LS', value: 9, color: 'yellow' }, | ||||||
|   { key: 8, text: '自定义', value: 8, color: 'pink' } |   { key: 10, text: 'AI Proxy', value: 10, color: 'purple' } | ||||||
| ]; | ]; | ||||||
|   | |||||||
| @@ -46,6 +46,9 @@ const EditChannel = () => { | |||||||
|     if (localInputs.base_url.endsWith('/')) { |     if (localInputs.base_url.endsWith('/')) { | ||||||
|       localInputs.base_url = localInputs.base_url.slice(0, localInputs.base_url.length - 1); |       localInputs.base_url = localInputs.base_url.slice(0, localInputs.base_url.length - 1); | ||||||
|     } |     } | ||||||
|  |     if (localInputs.type === 3 && localInputs.other === '') { | ||||||
|  |       localInputs.other = '2023-03-15-preview'; | ||||||
|  |     } | ||||||
|     let res; |     let res; | ||||||
|     if (isEdit) { |     if (isEdit) { | ||||||
|       res = await API.put(`/api/channel/`, { ...localInputs, id: parseInt(channelId) }); |       res = await API.put(`/api/channel/`, { ...localInputs, id: parseInt(channelId) }); | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| import React, { useEffect, useState } from 'react'; | import React, { useEffect, useState } from 'react'; | ||||||
| import { Button, Form, Grid, Header, Segment, Statistic } from 'semantic-ui-react'; | import { Button, Form, Grid, Header, Segment, Statistic } from 'semantic-ui-react'; | ||||||
| import { API, showError, showSuccess } from '../../helpers'; | import { API, showError, showInfo, showSuccess } from '../../helpers'; | ||||||
|  |  | ||||||
| const TopUp = () => { | const TopUp = () => { | ||||||
|   const [redemptionCode, setRedemptionCode] = useState(''); |   const [redemptionCode, setRedemptionCode] = useState(''); | ||||||
| @@ -9,6 +9,7 @@ const TopUp = () => { | |||||||
|  |  | ||||||
|   const topUp = async () => { |   const topUp = async () => { | ||||||
|     if (redemptionCode === '') { |     if (redemptionCode === '') { | ||||||
|  |       showInfo('请输入充值码!') | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|     const res = await API.post('/api/user/topup', { |     const res = await API.post('/api/user/topup', { | ||||||
| @@ -80,7 +81,7 @@ const TopUp = () => { | |||||||
|         <Grid.Column> |         <Grid.Column> | ||||||
|           <Statistic.Group widths='one'> |           <Statistic.Group widths='one'> | ||||||
|             <Statistic> |             <Statistic> | ||||||
|               <Statistic.Value>{userQuota}</Statistic.Value> |               <Statistic.Value>{userQuota.toLocaleString()}</Statistic.Value> | ||||||
|               <Statistic.Label>剩余额度</Statistic.Label> |               <Statistic.Label>剩余额度</Statistic.Label> | ||||||
|             </Statistic> |             </Statistic> | ||||||
|           </Statistic.Group> |           </Statistic.Group> | ||||||
|   | |||||||
| @@ -14,8 +14,9 @@ const EditUser = () => { | |||||||
|     github_id: '', |     github_id: '', | ||||||
|     wechat_id: '', |     wechat_id: '', | ||||||
|     email: '', |     email: '', | ||||||
|  |     quota: 0, | ||||||
|   }); |   }); | ||||||
|   const { username, display_name, password, github_id, wechat_id, email } = |   const { username, display_name, password, github_id, wechat_id, email, quota } = | ||||||
|     inputs; |     inputs; | ||||||
|   const handleInputChange = (e, { name, value }) => { |   const handleInputChange = (e, { name, value }) => { | ||||||
|     setInputs((inputs) => ({ ...inputs, [name]: value })); |     setInputs((inputs) => ({ ...inputs, [name]: value })); | ||||||
| @@ -44,7 +45,11 @@ const EditUser = () => { | |||||||
|   const submit = async () => { |   const submit = async () => { | ||||||
|     let res = undefined; |     let res = undefined; | ||||||
|     if (userId) { |     if (userId) { | ||||||
|       res = await API.put(`/api/user/`, { ...inputs, id: parseInt(userId) }); |       let data = { ...inputs, id: parseInt(userId) }; | ||||||
|  |       if (typeof data.quota === 'string') { | ||||||
|  |         data.quota = parseInt(data.quota); | ||||||
|  |       } | ||||||
|  |       res = await API.put(`/api/user/`, data); | ||||||
|     } else { |     } else { | ||||||
|       res = await API.put(`/api/user/self`, inputs); |       res = await API.put(`/api/user/self`, inputs); | ||||||
|     } |     } | ||||||
| @@ -92,6 +97,21 @@ const EditUser = () => { | |||||||
|               autoComplete='new-password' |               autoComplete='new-password' | ||||||
|             /> |             /> | ||||||
|           </Form.Field> |           </Form.Field> | ||||||
|  |           { | ||||||
|  |             userId && ( | ||||||
|  |               <Form.Field> | ||||||
|  |                 <Form.Input | ||||||
|  |                   label='剩余额度' | ||||||
|  |                   name='quota' | ||||||
|  |                   placeholder={'请输入新的剩余额度'} | ||||||
|  |                   onChange={handleInputChange} | ||||||
|  |                   value={quota} | ||||||
|  |                   type={'number'} | ||||||
|  |                   autoComplete='new-password' | ||||||
|  |                 /> | ||||||
|  |               </Form.Field> | ||||||
|  |             ) | ||||||
|  |           } | ||||||
|           <Form.Field> |           <Form.Field> | ||||||
|             <Form.Input |             <Form.Input | ||||||
|               label='已绑定的 GitHub 账户' |               label='已绑定的 GitHub 账户' | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user