mirror of
				https://github.com/songquanpeng/one-api.git
				synced 2025-10-31 05:43:42 +08:00 
			
		
		
		
	Compare commits
	
		
			6 Commits
		
	
	
		
			v0.4.2
			...
			v0.4.3-alp
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 7c7eb6b7ec | ||
|  | 8b2ef666ef | ||
|  | 955d5f8707 | ||
|  | 47ca449e32 | ||
|  | 39481eb6c0 | ||
|  | 69153e7231 | 
							
								
								
									
										10
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								README.md
									
									
									
									
									
								
							| @@ -200,4 +200,12 @@ https://openai.justsong.cn | |||||||
|    + 请检查你的令牌额度是否足够,这个和账户额度是分开的。 |    + 请检查你的令牌额度是否足够,这个和账户额度是分开的。 | ||||||
|    + 令牌额度仅供用户设置最大使用量,用户可自由设置。 |    + 令牌额度仅供用户设置最大使用量,用户可自由设置。 | ||||||
| 2. 宝塔部署后访问出现空白页面? | 2. 宝塔部署后访问出现空白页面? | ||||||
|    + 自动配置的问题,详见[#97](https://github.com/songquanpeng/one-api/issues/97)。 |    + 自动配置的问题,详见[#97](https://github.com/songquanpeng/one-api/issues/97)。 | ||||||
|  | 3. 提示无可用渠道? | ||||||
|  |    + 请检查的用户分组和渠道分组设置。 | ||||||
|  |    + 以及渠道的模型设置。 | ||||||
|  |  | ||||||
|  | ## 注意 | ||||||
|  | 本项目为开源项目,请在遵循 OpenAI 的[使用条款](https://openai.com/policies/terms-of-use)以及法律法规的情况下使用,不得用于非法用途。 | ||||||
|  |  | ||||||
|  | 本项目依据 MIT 协议开源,请以某种方式保留 One API 的版权信息。 | ||||||
|   | |||||||
| @@ -17,6 +17,7 @@ func GroupRatio2JSONString() string { | |||||||
| } | } | ||||||
|  |  | ||||||
| func UpdateGroupRatioByJSONString(jsonStr string) error { | func UpdateGroupRatioByJSONString(jsonStr string) error { | ||||||
|  | 	GroupRatio = make(map[string]float64) | ||||||
| 	return json.Unmarshal([]byte(jsonStr), &GroupRatio) | 	return json.Unmarshal([]byte(jsonStr), &GroupRatio) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -39,6 +39,7 @@ func ModelRatio2JSONString() string { | |||||||
| } | } | ||||||
|  |  | ||||||
| func UpdateModelRatioByJSONString(jsonStr string) error { | func UpdateModelRatioByJSONString(jsonStr string) error { | ||||||
|  | 	ModelRatio = make(map[string]float64) | ||||||
| 	return json.Unmarshal([]byte(jsonStr), &ModelRatio) | 	return json.Unmarshal([]byte(jsonStr), &ModelRatio) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -37,6 +37,58 @@ type OpenAIUsageResponse struct { | |||||||
| 	TotalUsage float64 `json:"total_usage"` // unit: 0.01 dollar | 	TotalUsage float64 `json:"total_usage"` // unit: 0.01 dollar | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type OpenAISBUsageResponse struct { | ||||||
|  | 	Msg  string `json:"msg"` | ||||||
|  | 	Data *struct { | ||||||
|  | 		Credit string `json:"credit"` | ||||||
|  | 	} `json:"data"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func GetResponseBody(method, url string, channel *model.Channel) ([]byte, error) { | ||||||
|  | 	client := &http.Client{} | ||||||
|  | 	req, err := http.NewRequest(method, url, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	auth := fmt.Sprintf("Bearer %s", channel.Key) | ||||||
|  | 	req.Header.Add("Authorization", auth) | ||||||
|  | 	res, err := client.Do(req) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	body, err := io.ReadAll(res.Body) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	err = res.Body.Close() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return body, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func updateChannelOpenAISBBalance(channel *model.Channel) (float64, error) { | ||||||
|  | 	url := fmt.Sprintf("https://api.openai-sb.com/sb-api/user/status?api_key=%s", channel.Key) | ||||||
|  | 	body, err := GetResponseBody("GET", url, channel) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, err | ||||||
|  | 	} | ||||||
|  | 	response := OpenAISBUsageResponse{} | ||||||
|  | 	err = json.Unmarshal(body, &response) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, err | ||||||
|  | 	} | ||||||
|  | 	if response.Data == nil { | ||||||
|  | 		return 0, errors.New(response.Msg) | ||||||
|  | 	} | ||||||
|  | 	balance, err := strconv.ParseFloat(response.Data.Credit, 64) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, err | ||||||
|  | 	} | ||||||
|  | 	channel.UpdateBalance(balance) | ||||||
|  | 	return balance, nil | ||||||
|  | } | ||||||
|  |  | ||||||
| func updateChannelBalance(channel *model.Channel) (float64, error) { | func updateChannelBalance(channel *model.Channel) (float64, error) { | ||||||
| 	baseURL := common.ChannelBaseURLs[channel.Type] | 	baseURL := common.ChannelBaseURLs[channel.Type] | ||||||
| 	switch channel.Type { | 	switch channel.Type { | ||||||
| @@ -48,27 +100,14 @@ func updateChannelBalance(channel *model.Channel) (float64, error) { | |||||||
| 		return 0, errors.New("尚未实现") | 		return 0, errors.New("尚未实现") | ||||||
| 	case common.ChannelTypeCustom: | 	case common.ChannelTypeCustom: | ||||||
| 		baseURL = channel.BaseURL | 		baseURL = channel.BaseURL | ||||||
|  | 	case common.ChannelTypeOpenAISB: | ||||||
|  | 		return updateChannelOpenAISBBalance(channel) | ||||||
| 	default: | 	default: | ||||||
| 		return 0, errors.New("尚未实现") | 		return 0, errors.New("尚未实现") | ||||||
| 	} | 	} | ||||||
| 	url := fmt.Sprintf("%s/v1/dashboard/billing/subscription", baseURL) | 	url := fmt.Sprintf("%s/v1/dashboard/billing/subscription", baseURL) | ||||||
|  |  | ||||||
| 	client := &http.Client{} | 	body, err := GetResponseBody("GET", url, channel) | ||||||
| 	req, err := http.NewRequest("GET", url, nil) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return 0, err |  | ||||||
| 	} |  | ||||||
| 	auth := fmt.Sprintf("Bearer %s", channel.Key) |  | ||||||
| 	req.Header.Add("Authorization", auth) |  | ||||||
| 	res, err := client.Do(req) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return 0, err |  | ||||||
| 	} |  | ||||||
| 	body, err := io.ReadAll(res.Body) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return 0, err |  | ||||||
| 	} |  | ||||||
| 	err = res.Body.Close() |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return 0, err | 		return 0, err | ||||||
| 	} | 	} | ||||||
| @@ -84,20 +123,7 @@ func updateChannelBalance(channel *model.Channel) (float64, error) { | |||||||
| 		startDate = now.AddDate(0, 0, -100).Format("2006-01-02") | 		startDate = now.AddDate(0, 0, -100).Format("2006-01-02") | ||||||
| 	} | 	} | ||||||
| 	url = fmt.Sprintf("%s/v1/dashboard/billing/usage?start_date=%s&end_date=%s", baseURL, startDate, endDate) | 	url = fmt.Sprintf("%s/v1/dashboard/billing/usage?start_date=%s&end_date=%s", baseURL, startDate, endDate) | ||||||
| 	req, err = http.NewRequest("GET", url, nil) | 	body, err = GetResponseBody("GET", url, channel) | ||||||
| 	if err != nil { |  | ||||||
| 		return 0, err |  | ||||||
| 	} |  | ||||||
| 	req.Header.Add("Authorization", auth) |  | ||||||
| 	res, err = client.Do(req) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return 0, err |  | ||||||
| 	} |  | ||||||
| 	body, err = io.ReadAll(res.Body) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return 0, err |  | ||||||
| 	} |  | ||||||
| 	err = res.Body.Close() |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return 0, err | 		return 0, err | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -58,6 +58,20 @@ func countTokenMessages(messages []Message, model string) int { | |||||||
| 	return tokenNum | 	return tokenNum | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func countTokenInput(input any, model string) int { | ||||||
|  | 	switch input.(type) { | ||||||
|  | 	case string: | ||||||
|  | 		return countTokenText(input.(string), model) | ||||||
|  | 	case []string: | ||||||
|  | 		text := "" | ||||||
|  | 		for _, s := range input.([]string) { | ||||||
|  | 			text += s | ||||||
|  | 		} | ||||||
|  | 		return countTokenText(text, model) | ||||||
|  | 	} | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  |  | ||||||
| func countTokenText(text string, model string) int { | func countTokenText(text string, model string) int { | ||||||
| 	tokenEncoder := getTokenEncoder(model) | 	tokenEncoder := getTokenEncoder(model) | ||||||
| 	token := tokenEncoder.Encode(text, nil, nil) | 	token := tokenEncoder.Encode(text, nil, nil) | ||||||
|   | |||||||
| @@ -38,7 +38,7 @@ type GeneralOpenAIRequest struct { | |||||||
| 	Temperature float64   `json:"temperature"` | 	Temperature float64   `json:"temperature"` | ||||||
| 	TopP        float64   `json:"top_p"` | 	TopP        float64   `json:"top_p"` | ||||||
| 	N           int       `json:"n"` | 	N           int       `json:"n"` | ||||||
| 	Input       string    `json:"input"` | 	Input       any       `json:"input"` | ||||||
| } | } | ||||||
|  |  | ||||||
| type ChatRequest struct { | type ChatRequest struct { | ||||||
| @@ -189,7 +189,7 @@ func relayHelper(c *gin.Context, relayMode int) *OpenAIErrorWithStatusCode { | |||||||
| 	case RelayModeCompletions: | 	case RelayModeCompletions: | ||||||
| 		promptTokens = countTokenText(textRequest.Prompt, textRequest.Model) | 		promptTokens = countTokenText(textRequest.Prompt, textRequest.Model) | ||||||
| 	case RelayModeModeration: | 	case RelayModeModeration: | ||||||
| 		promptTokens = countTokenText(textRequest.Input, textRequest.Model) | 		promptTokens = countTokenInput(textRequest.Input, textRequest.Model) | ||||||
| 	} | 	} | ||||||
| 	preConsumedTokens := common.PreConsumedQuota | 	preConsumedTokens := common.PreConsumedQuota | ||||||
| 	if textRequest.MaxTokens != 0 { | 	if textRequest.MaxTokens != 0 { | ||||||
|   | |||||||
| @@ -27,6 +27,13 @@ function renderType(type) { | |||||||
|   return <Label basic color={type2label[type].color}>{type2label[type].text}</Label>; |   return <Label basic color={type2label[type].color}>{type2label[type].text}</Label>; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | function renderBalance(type, balance) { | ||||||
|  |   if (type === 5) { | ||||||
|  |     return <span>¥{(balance / 10000).toFixed(2)}</span> | ||||||
|  |   } | ||||||
|  |   return <span>${balance.toFixed(2)}</span> | ||||||
|  | } | ||||||
|  |  | ||||||
| const ChannelsTable = () => { | const ChannelsTable = () => { | ||||||
|   const [channels, setChannels] = useState([]); |   const [channels, setChannels] = useState([]); | ||||||
|   const [loading, setLoading] = useState(true); |   const [loading, setLoading] = useState(true); | ||||||
| @@ -336,7 +343,7 @@ const ChannelsTable = () => { | |||||||
|                     <Popup |                     <Popup | ||||||
|                       content={channel.balance_updated_time ? renderTimestamp(channel.balance_updated_time) : '未更新'} |                       content={channel.balance_updated_time ? renderTimestamp(channel.balance_updated_time) : '未更新'} | ||||||
|                       key={channel.id} |                       key={channel.id} | ||||||
|                       trigger={<span>${channel.balance.toFixed(2)}</span>} |                       trigger={renderBalance(channel.type, channel.balance)} | ||||||
|                       basic |                       basic | ||||||
|                     /> |                     /> | ||||||
|                   </Table.Cell> |                   </Table.Cell> | ||||||
|   | |||||||
| @@ -61,7 +61,7 @@ const EditChannel = () => { | |||||||
|  |  | ||||||
|   const fetchGroups = async () => { |   const fetchGroups = async () => { | ||||||
|     try { |     try { | ||||||
|       let res = await API.get(`/api/group`); |       let res = await API.get(`/api/group/`); | ||||||
|       setGroupOptions(res.data.data.map((group) => ({ |       setGroupOptions(res.data.data.map((group) => ({ | ||||||
|         key: group, |         key: group, | ||||||
|         text: group, |         text: group, | ||||||
|   | |||||||
| @@ -25,7 +25,7 @@ const EditUser = () => { | |||||||
|   }; |   }; | ||||||
|   const fetchGroups = async () => { |   const fetchGroups = async () => { | ||||||
|     try { |     try { | ||||||
|       let res = await API.get(`/api/group`); |       let res = await API.get(`/api/group/`); | ||||||
|       setGroupOptions(res.data.data.map((group) => ({ |       setGroupOptions(res.data.data.map((group) => ({ | ||||||
|         key: group, |         key: group, | ||||||
|         text: group, |         text: group, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user