mirror of
				https://github.com/songquanpeng/one-api.git
				synced 2025-11-04 07:43:41 +08:00 
			
		
		
		
	Compare commits
	
		
			5 Commits
		
	
	
		
			v0.6.9-alp
			...
			v0.6.9-alp
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					fdd7bf41c0 | ||
| 
						 | 
					29389ed44f | ||
| 
						 | 
					88acc5a614 | ||
| 
						 | 
					a21681096a | ||
| 
						 | 
					32f90a79a8 | 
@@ -31,15 +31,15 @@ func UnmarshalBodyReusable(c *gin.Context, v any) error {
 | 
			
		||||
	contentType := c.Request.Header.Get("Content-Type")
 | 
			
		||||
	if strings.HasPrefix(contentType, "application/json") {
 | 
			
		||||
		err = json.Unmarshal(requestBody, &v)
 | 
			
		||||
		c.Request.Body = io.NopCloser(bytes.NewBuffer(requestBody))
 | 
			
		||||
	} else {
 | 
			
		||||
		// skip for now
 | 
			
		||||
		// TODO: someday non json request have variant model, we will need to implementation this
 | 
			
		||||
		c.Request.Body = io.NopCloser(bytes.NewBuffer(requestBody))
 | 
			
		||||
		err = c.ShouldBind(&v)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	// Reset request body
 | 
			
		||||
	c.Request.Body = io.NopCloser(bytes.NewBuffer(requestBody))
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -81,6 +81,26 @@ type APGC2DGPTUsageResponse struct {
 | 
			
		||||
	TotalUsed      float64 `json:"total_used"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SiliconFlowUsageResponse struct {
 | 
			
		||||
	Code    int    `json:"code"`
 | 
			
		||||
	Message string `json:"message"`
 | 
			
		||||
	Status  bool   `json:"status"`
 | 
			
		||||
	Data    struct {
 | 
			
		||||
		ID            string `json:"id"`
 | 
			
		||||
		Name          string `json:"name"`
 | 
			
		||||
		Image         string `json:"image"`
 | 
			
		||||
		Email         string `json:"email"`
 | 
			
		||||
		IsAdmin       bool   `json:"isAdmin"`
 | 
			
		||||
		Balance       string `json:"balance"`
 | 
			
		||||
		Status        string `json:"status"`
 | 
			
		||||
		Introduction  string `json:"introduction"`
 | 
			
		||||
		Role          string `json:"role"`
 | 
			
		||||
		ChargeBalance string `json:"chargeBalance"`
 | 
			
		||||
		TotalBalance  string `json:"totalBalance"`
 | 
			
		||||
		Category      string `json:"category"`
 | 
			
		||||
	} `json:"data"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetAuthHeader get auth header
 | 
			
		||||
func GetAuthHeader(token string) http.Header {
 | 
			
		||||
	h := http.Header{}
 | 
			
		||||
@@ -203,6 +223,28 @@ func updateChannelAIGC2DBalance(channel *model.Channel) (float64, error) {
 | 
			
		||||
	return response.TotalAvailable, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func updateChannelSiliconFlowBalance(channel *model.Channel) (float64, error) {
 | 
			
		||||
	url := "https://api.siliconflow.cn/v1/user/info"
 | 
			
		||||
	body, err := GetResponseBody("GET", url, channel, GetAuthHeader(channel.Key))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	response := SiliconFlowUsageResponse{}
 | 
			
		||||
	err = json.Unmarshal(body, &response)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	if response.Code != 20000 {
 | 
			
		||||
		return 0, fmt.Errorf("code: %d, message: %s", response.Code, response.Message)
 | 
			
		||||
	}
 | 
			
		||||
	balance, err := strconv.ParseFloat(response.Data.Balance, 64)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	channel.UpdateBalance(balance)
 | 
			
		||||
	return balance, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func updateChannelBalance(channel *model.Channel) (float64, error) {
 | 
			
		||||
	baseURL := channeltype.ChannelBaseURLs[channel.Type]
 | 
			
		||||
	if channel.GetBaseURL() == "" {
 | 
			
		||||
@@ -227,6 +269,8 @@ func updateChannelBalance(channel *model.Channel) (float64, error) {
 | 
			
		||||
		return updateChannelAPI2GPTBalance(channel)
 | 
			
		||||
	case channeltype.AIGC2D:
 | 
			
		||||
		return updateChannelAIGC2DBalance(channel)
 | 
			
		||||
	case channeltype.SiliconFlow:
 | 
			
		||||
		return updateChannelSiliconFlowBalance(channel)
 | 
			
		||||
	default:
 | 
			
		||||
		return 0, errors.New("尚未实现")
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ModelRequest struct {
 | 
			
		||||
	Model string `json:"model"`
 | 
			
		||||
	Model string `json:"model" form:"model"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Distribute() func(c *gin.Context) {
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ type Token struct {
 | 
			
		||||
	RemainQuota    int64   `json:"remain_quota" gorm:"bigint;default:0"`
 | 
			
		||||
	UnlimitedQuota bool    `json:"unlimited_quota" gorm:"default:false"`
 | 
			
		||||
	UsedQuota      int64   `json:"used_quota" gorm:"bigint;default:0"` // used quota
 | 
			
		||||
	Models         *string `json:"models" gorm:"default:''"`           // allowed models
 | 
			
		||||
	Models         *string `json:"models" gorm:"type:text"`            // allowed models
 | 
			
		||||
	Subnet         *string `json:"subnet" gorm:"default:''"`           // allowed subnet
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -121,30 +121,40 @@ func GetTokenById(id int) (*Token, error) {
 | 
			
		||||
	return &token, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (token *Token) Insert() error {
 | 
			
		||||
func (t *Token) Insert() error {
 | 
			
		||||
	var err error
 | 
			
		||||
	err = DB.Create(token).Error
 | 
			
		||||
	err = DB.Create(t).Error
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Update Make sure your token's fields is completed, because this will update non-zero values
 | 
			
		||||
func (token *Token) Update() error {
 | 
			
		||||
func (t *Token) Update() error {
 | 
			
		||||
	var err error
 | 
			
		||||
	err = DB.Model(token).Select("name", "status", "expired_time", "remain_quota", "unlimited_quota", "models", "subnet").Updates(token).Error
 | 
			
		||||
	err = DB.Model(t).Select("name", "status", "expired_time", "remain_quota", "unlimited_quota", "models", "subnet").Updates(t).Error
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (token *Token) SelectUpdate() error {
 | 
			
		||||
func (t *Token) SelectUpdate() error {
 | 
			
		||||
	// This can update zero values
 | 
			
		||||
	return DB.Model(token).Select("accessed_time", "status").Updates(token).Error
 | 
			
		||||
	return DB.Model(t).Select("accessed_time", "status").Updates(t).Error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (token *Token) Delete() error {
 | 
			
		||||
func (t *Token) Delete() error {
 | 
			
		||||
	var err error
 | 
			
		||||
	err = DB.Delete(token).Error
 | 
			
		||||
	err = DB.Delete(t).Error
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Token) GetModels() string {
 | 
			
		||||
	if t == nil {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	if t.Models == nil {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	return *t.Models
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func DeleteTokenById(id int, userId int) (err error) {
 | 
			
		||||
	// Why we need userId here? In case user want to delete other's token.
 | 
			
		||||
	if id == 0 || userId == 0 {
 | 
			
		||||
 
 | 
			
		||||
@@ -55,8 +55,8 @@ func StreamHandler(c *gin.Context, resp *http.Response, relayMode int) (*model.E
 | 
			
		||||
				render.StringData(c, data) // if error happened, pass the data to client
 | 
			
		||||
				continue                   // just ignore the error
 | 
			
		||||
			}
 | 
			
		||||
			if len(streamResponse.Choices) == 0 {
 | 
			
		||||
				// but for empty choice, we should not pass it to client, this is for azure
 | 
			
		||||
			if len(streamResponse.Choices) == 0 && streamResponse.Usage == nil {
 | 
			
		||||
				// but for empty choice and no usage, we should not pass it to client, this is for azure
 | 
			
		||||
				continue // just ignore empty choice
 | 
			
		||||
			}
 | 
			
		||||
			render.StringData(c, data)
 | 
			
		||||
 
 | 
			
		||||
@@ -5,4 +5,5 @@ var ModelList = []string{
 | 
			
		||||
	"hunyuan-standard",
 | 
			
		||||
	"hunyuan-standard-256K",
 | 
			
		||||
	"hunyuan-pro",
 | 
			
		||||
	"hunyuan-vision",
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ var ModelList = []string{
 | 
			
		||||
	"SparkDesk-v1.1",
 | 
			
		||||
	"SparkDesk-v2.1",
 | 
			
		||||
	"SparkDesk-v3.1",
 | 
			
		||||
	"SparkDesk-v3.1-128K",
 | 
			
		||||
	"SparkDesk-v3.5",
 | 
			
		||||
	"SparkDesk-v4.0",
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -272,9 +272,9 @@ func xunfeiMakeRequest(textRequest model.GeneralOpenAIRequest, domain, authUrl,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseAPIVersionByModelName(modelName string) string {
 | 
			
		||||
	parts := strings.Split(modelName, "-")
 | 
			
		||||
	if len(parts) == 2 {
 | 
			
		||||
		return parts[1]
 | 
			
		||||
	index := strings.IndexAny(modelName, "-")
 | 
			
		||||
	if index != -1 {
 | 
			
		||||
		return modelName[index+1:]
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
@@ -288,6 +288,8 @@ func apiVersion2domain(apiVersion string) string {
 | 
			
		||||
		return "generalv2"
 | 
			
		||||
	case "v3.1":
 | 
			
		||||
		return "generalv3"
 | 
			
		||||
	case "v3.1-128K":
 | 
			
		||||
		return "pro-128k"
 | 
			
		||||
	case "v3.5":
 | 
			
		||||
		return "generalv3.5"
 | 
			
		||||
	case "v4.0":
 | 
			
		||||
@@ -297,7 +299,14 @@ func apiVersion2domain(apiVersion string) string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getXunfeiAuthUrl(apiVersion string, apiKey string, apiSecret string) (string, string) {
 | 
			
		||||
	var authUrl string
 | 
			
		||||
	domain := apiVersion2domain(apiVersion)
 | 
			
		||||
	authUrl := buildXunfeiAuthUrl(fmt.Sprintf("wss://spark-api.xf-yun.com/%s/chat", apiVersion), apiKey, apiSecret)
 | 
			
		||||
	switch apiVersion {
 | 
			
		||||
	case "v3.1-128K":
 | 
			
		||||
		authUrl = buildXunfeiAuthUrl(fmt.Sprintf("wss://spark-api.xf-yun.com/%s/pro-128k", apiVersion), apiKey, apiSecret)
 | 
			
		||||
		break
 | 
			
		||||
	default:
 | 
			
		||||
		authUrl = buildXunfeiAuthUrl(fmt.Sprintf("wss://spark-api.xf-yun.com/%s/chat", apiVersion), apiKey, apiSecret)
 | 
			
		||||
	}
 | 
			
		||||
	return domain, authUrl
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -128,6 +128,7 @@ var ModelRatio = map[string]float64{
 | 
			
		||||
	"SparkDesk-v1.1":            1.2858, // ¥0.018 / 1k tokens
 | 
			
		||||
	"SparkDesk-v2.1":            1.2858, // ¥0.018 / 1k tokens
 | 
			
		||||
	"SparkDesk-v3.1":            1.2858, // ¥0.018 / 1k tokens
 | 
			
		||||
	"SparkDesk-v3.1-128K":       1.2858, // ¥0.018 / 1k tokens
 | 
			
		||||
	"SparkDesk-v3.5":            1.2858, // ¥0.018 / 1k tokens
 | 
			
		||||
	"SparkDesk-v4.0":            1.2858, // ¥0.018 / 1k tokens
 | 
			
		||||
	"360GPT_S2_V9":              0.8572, // ¥0.012 / 1k tokens
 | 
			
		||||
 
 | 
			
		||||
@@ -78,7 +78,7 @@ const EditChannel = (props) => {
 | 
			
		||||
                    localModels = ['chatglm_pro', 'chatglm_std', 'chatglm_lite'];
 | 
			
		||||
                    break;
 | 
			
		||||
                case 18:
 | 
			
		||||
                    localModels = ['SparkDesk', 'SparkDesk-v1.1', 'SparkDesk-v2.1', 'SparkDesk-v3.1', 'SparkDesk-v3.5', 'SparkDesk-v4.0'];
 | 
			
		||||
                    localModels = ['SparkDesk', 'SparkDesk-v1.1', 'SparkDesk-v2.1', 'SparkDesk-v3.1', 'SparkDesk-v3.1-128K', 'SparkDesk-v3.5', 'SparkDesk-v4.0'];
 | 
			
		||||
                    break;
 | 
			
		||||
                case 19:
 | 
			
		||||
                    localModels = ['360GPT_S2_V9', 'embedding-bert-512-v1', 'embedding_s1_v1', 'semantic_similarity_s1_v1'];
 | 
			
		||||
 
 | 
			
		||||
@@ -268,6 +268,8 @@ function renderBalance(type, balance) {
 | 
			
		||||
      return <span>¥{balance.toFixed(2)}</span>;
 | 
			
		||||
    case 13: // AIGC2D
 | 
			
		||||
      return <span>{renderNumber(balance)}</span>;
 | 
			
		||||
    case 44: // SiliconFlow
 | 
			
		||||
      return <span>¥{balance.toFixed(2)}</span>;
 | 
			
		||||
    default:
 | 
			
		||||
      return <span>不支持</span>;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -91,7 +91,7 @@ const typeConfig = {
 | 
			
		||||
      other: '版本号'
 | 
			
		||||
    },
 | 
			
		||||
    input: {
 | 
			
		||||
      models: ['SparkDesk', 'SparkDesk-v1.1', 'SparkDesk-v2.1', 'SparkDesk-v3.1', 'SparkDesk-v3.5', 'SparkDesk-v4.0']
 | 
			
		||||
      models: ['SparkDesk', 'SparkDesk-v1.1', 'SparkDesk-v2.1', 'SparkDesk-v3.1', 'SparkDesk-v3.1-128K', 'SparkDesk-v3.5', 'SparkDesk-v4.0']
 | 
			
		||||
    },
 | 
			
		||||
    prompt: {
 | 
			
		||||
      key: '按照如下格式输入:APPID|APISecret|APIKey',
 | 
			
		||||
 
 | 
			
		||||
@@ -52,6 +52,8 @@ function renderBalance(type, balance) {
 | 
			
		||||
      return <span>¥{balance.toFixed(2)}</span>;
 | 
			
		||||
    case 13: // AIGC2D
 | 
			
		||||
      return <span>{renderNumber(balance)}</span>;
 | 
			
		||||
    case 44: // SiliconFlow
 | 
			
		||||
      return <span>¥{balance.toFixed(2)}</span>;
 | 
			
		||||
    default:
 | 
			
		||||
      return <span>不支持</span>;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user