fix: whisper model billing

- Refactor model name handling across multiple controllers to improve clarity and maintainability.
- Enhance error logging and handling for better debugging and request processing robustness.
- Update pricing models in accordance with new calculations, ensuring accuracy in the billing logic.
This commit is contained in:
Laisky.Cai 2025-01-26 08:02:55 +00:00
parent f1db73405e
commit 5e351bc02a
6 changed files with 28 additions and 21 deletions

View File

@ -60,7 +60,6 @@ func (a *Adaptor) GetChannelName() string {
func (a *Adaptor) GetRequestURL(meta *meta.Meta) (string, error) { func (a *Adaptor) GetRequestURL(meta *meta.Meta) (string, error) {
prefix := fmt.Sprintf("/v1/oneapi/proxy/%d", meta.ChannelId) prefix := fmt.Sprintf("/v1/oneapi/proxy/%d", meta.ChannelId)
return meta.BaseURL + strings.TrimPrefix(meta.RequestURLPath, prefix), nil return meta.BaseURL + strings.TrimPrefix(meta.RequestURLPath, prefix), nil
} }
func (a *Adaptor) SetupRequestHeader(c *gin.Context, req *http.Request, meta *meta.Meta) error { func (a *Adaptor) SetupRequestHeader(c *gin.Context, req *http.Request, meta *meta.Meta) error {

View File

@ -71,7 +71,7 @@ var ModelRatio = map[string]float64{
"text-davinci-003": 10, "text-davinci-003": 10,
"text-davinci-edit-001": 10, "text-davinci-edit-001": 10,
"code-davinci-edit-001": 10, "code-davinci-edit-001": 10,
"whisper-1": 15, // $0.006 / minute -> $0.006 / 150 words -> $0.006 / 200 tokens -> $0.03 / 1k tokens "whisper-1": 15,
"tts-1": 7.5, // $0.015 / 1K characters "tts-1": 7.5, // $0.015 / 1K characters
"tts-1-1106": 7.5, "tts-1-1106": 7.5,
"tts-1-hd": 15, // $0.030 / 1K characters "tts-1-hd": 15, // $0.030 / 1K characters
@ -385,8 +385,9 @@ func GetAudioCompletionRatio(actualModelName string) float64 {
// AudioTokensPerSecond is the number of audio tokens per second for each model. // AudioTokensPerSecond is the number of audio tokens per second for each model.
var AudioPromptTokensPerSecond = map[string]float64{ var AudioPromptTokensPerSecond = map[string]float64{
// $0.006 / minute -> $0.002 / 20 seconds -> $0.002 / 1K tokens // whisper 的 API 价格是 $0.0001/sec。one-api 的历史倍率为 15对应 $0.03/kilo_tokens。
"whisper-1": 1000 / 20, // 那么换算后可得,每秒的 tokens 应该为 0.0001/0.03*1000 = 3.3333
"whisper-1": 0.0001 / 0.03 * 1000,
// gpt-4o-audio series processes 10 tokens per second // gpt-4o-audio series processes 10 tokens per second
"gpt-4o-audio-preview": 10, "gpt-4o-audio-preview": 10,
"gpt-4o-audio-preview-2024-12-17": 10, "gpt-4o-audio-preview-2024-12-17": 10,

View File

@ -135,17 +135,6 @@ func postConsumeQuota(ctx context.Context, usage *relaymodel.Usage, meta *meta.M
return quota return quota
} }
func getMappedModelName(modelName string, mapping map[string]string) (string, bool) {
if mapping == nil {
return modelName, false
}
mappedModelName := mapping[modelName]
if mappedModelName != "" {
return mappedModelName, true
}
return modelName, false
}
func isErrorHappened(meta *meta.Meta, resp *http.Response) bool { func isErrorHappened(meta *meta.Meta, resp *http.Response) bool {
if resp == nil { if resp == nil {
if meta.ChannelType == channeltype.AwsClaude { if meta.ChannelType == channeltype.AwsClaude {

View File

@ -116,7 +116,8 @@ func RelayImageHelper(c *gin.Context, relayMode int) *relaymodel.ErrorWithStatus
// map model name // map model name
var isModelMapped bool var isModelMapped bool
meta.OriginModelName = imageRequest.Model meta.OriginModelName = imageRequest.Model
imageRequest.Model, isModelMapped = getMappedModelName(imageRequest.Model, meta.ModelMapping) imageRequest.Model = meta.ActualModelName
isModelMapped = meta.OriginModelName != meta.ActualModelName
meta.ActualModelName = imageRequest.Model meta.ActualModelName = imageRequest.Model
metalib.Set2Context(c, meta) metalib.Set2Context(c, meta)
@ -133,7 +134,7 @@ func RelayImageHelper(c *gin.Context, relayMode int) *relaymodel.ErrorWithStatus
imageModel := imageRequest.Model imageModel := imageRequest.Model
// Convert the original image model // Convert the original image model
imageRequest.Model, _ = getMappedModelName(imageRequest.Model, billingratio.ImageOriginModelName) imageRequest.Model = metalib.GetMappedModelName(imageRequest.Model, billingratio.ImageOriginModelName)
c.Set("response_format", imageRequest.ResponseFormat) c.Set("response_format", imageRequest.ResponseFormat)
var requestBody io.Reader var requestBody io.Reader

View File

@ -21,13 +21,13 @@ import (
"github.com/songquanpeng/one-api/relay/billing" "github.com/songquanpeng/one-api/relay/billing"
billingratio "github.com/songquanpeng/one-api/relay/billing/ratio" billingratio "github.com/songquanpeng/one-api/relay/billing/ratio"
"github.com/songquanpeng/one-api/relay/channeltype" "github.com/songquanpeng/one-api/relay/channeltype"
"github.com/songquanpeng/one-api/relay/meta" metalib "github.com/songquanpeng/one-api/relay/meta"
relaymodel "github.com/songquanpeng/one-api/relay/model" relaymodel "github.com/songquanpeng/one-api/relay/model"
) )
func RelayTextHelper(c *gin.Context) *relaymodel.ErrorWithStatusCode { func RelayTextHelper(c *gin.Context) *relaymodel.ErrorWithStatusCode {
ctx := c.Request.Context() ctx := c.Request.Context()
meta := meta.GetByContext(c) meta := metalib.GetByContext(c)
// get & validate textRequest // get & validate textRequest
textRequest, err := getAndValidateTextRequest(c, meta.Mode) textRequest, err := getAndValidateTextRequest(c, meta.Mode)
if err != nil { if err != nil {
@ -38,7 +38,7 @@ func RelayTextHelper(c *gin.Context) *relaymodel.ErrorWithStatusCode {
// map model name // map model name
meta.OriginModelName = textRequest.Model meta.OriginModelName = textRequest.Model
textRequest.Model, _ = getMappedModelName(textRequest.Model, meta.ModelMapping) textRequest.Model = meta.ActualModelName
meta.ActualModelName = textRequest.Model meta.ActualModelName = textRequest.Model
// set system prompt if not empty // set system prompt if not empty
systemPromptReset := setSystemPrompt(ctx, textRequest, meta.SystemPrompt) systemPromptReset := setSystemPrompt(ctx, textRequest, meta.SystemPrompt)
@ -117,7 +117,7 @@ func RelayTextHelper(c *gin.Context) *relaymodel.ErrorWithStatusCode {
return nil return nil
} }
func getRequestBody(c *gin.Context, meta *meta.Meta, textRequest *relaymodel.GeneralOpenAIRequest, adaptor adaptor.Adaptor) (io.Reader, error) { func getRequestBody(c *gin.Context, meta *metalib.Meta, textRequest *relaymodel.GeneralOpenAIRequest, adaptor adaptor.Adaptor) (io.Reader, error) {
if !config.EnforceIncludeUsage && if !config.EnforceIncludeUsage &&
meta.APIType == apitype.OpenAI && meta.APIType == apitype.OpenAI &&
meta.OriginModelName == meta.ActualModelName && meta.OriginModelName == meta.ActualModelName &&

View File

@ -35,6 +35,20 @@ type Meta struct {
SystemPrompt string SystemPrompt string
} }
// GetMappedModelName returns the mapped model name and a bool indicating if the model name is mapped
func GetMappedModelName(modelName string, mapping map[string]string) string {
if mapping == nil {
return modelName
}
mappedModelName := mapping[modelName]
if mappedModelName != "" {
return mappedModelName
}
return modelName
}
func GetByContext(c *gin.Context) *Meta { func GetByContext(c *gin.Context) *Meta {
if v, ok := c.Get(ctxkey.Meta); ok { if v, ok := c.Get(ctxkey.Meta); ok {
return v.(*Meta) return v.(*Meta)
@ -50,6 +64,7 @@ func GetByContext(c *gin.Context) *Meta {
Group: c.GetString(ctxkey.Group), Group: c.GetString(ctxkey.Group),
ModelMapping: c.GetStringMapString(ctxkey.ModelMapping), ModelMapping: c.GetStringMapString(ctxkey.ModelMapping),
OriginModelName: c.GetString(ctxkey.RequestModel), OriginModelName: c.GetString(ctxkey.RequestModel),
ActualModelName: c.GetString(ctxkey.RequestModel),
BaseURL: c.GetString(ctxkey.BaseURL), BaseURL: c.GetString(ctxkey.BaseURL),
APIKey: strings.TrimPrefix(c.Request.Header.Get("Authorization"), "Bearer "), APIKey: strings.TrimPrefix(c.Request.Header.Get("Authorization"), "Bearer "),
RequestURLPath: c.Request.URL.String(), RequestURLPath: c.Request.URL.String(),
@ -65,6 +80,8 @@ func GetByContext(c *gin.Context) *Meta {
} }
meta.APIType = channeltype.ToAPIType(meta.ChannelType) meta.APIType = channeltype.ToAPIType(meta.ChannelType)
meta.ActualModelName = GetMappedModelName(meta.OriginModelName, meta.ModelMapping)
Set2Context(c, &meta) Set2Context(c, &meta)
return &meta return &meta
} }