mirror of
https://github.com/songquanpeng/one-api.git
synced 2025-09-18 17:46:37 +08:00
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:
parent
ca9aaaf07d
commit
010bc72304
@ -21,4 +21,5 @@ const (
|
|||||||
AvailableModels = "available_models"
|
AvailableModels = "available_models"
|
||||||
KeyRequestBody = "key_request_body"
|
KeyRequestBody = "key_request_body"
|
||||||
SystemPrompt = "system_prompt"
|
SystemPrompt = "system_prompt"
|
||||||
|
Meta = "meta"
|
||||||
)
|
)
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
@ -380,8 +380,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,
|
||||||
|
@ -129,17 +129,6 @@ func postConsumeQuota(ctx context.Context, usage *relaymodel.Usage, meta *meta.M
|
|||||||
model.UpdateChannelUsedQuota(meta.ChannelId, quota)
|
model.UpdateChannelUsedQuota(meta.ChannelId, 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 {
|
||||||
|
@ -18,7 +18,7 @@ import (
|
|||||||
"github.com/songquanpeng/one-api/relay/adaptor/openai"
|
"github.com/songquanpeng/one-api/relay/adaptor/openai"
|
||||||
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"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ func getImageSizeRatio(model string, size string) float64 {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateImageRequest(imageRequest *relaymodel.ImageRequest, _ *meta.Meta) *relaymodel.ErrorWithStatusCode {
|
func validateImageRequest(imageRequest *relaymodel.ImageRequest, _ *metalib.Meta) *relaymodel.ErrorWithStatusCode {
|
||||||
// check prompt length
|
// check prompt length
|
||||||
if imageRequest.Prompt == "" {
|
if imageRequest.Prompt == "" {
|
||||||
return openai.ErrorWrapper(errors.New("prompt is required"), "prompt_missing", http.StatusBadRequest)
|
return openai.ErrorWrapper(errors.New("prompt is required"), "prompt_missing", http.StatusBadRequest)
|
||||||
@ -104,7 +104,7 @@ func getImageCostRatio(imageRequest *relaymodel.ImageRequest) (float64, error) {
|
|||||||
|
|
||||||
func RelayImageHelper(c *gin.Context, relayMode int) *relaymodel.ErrorWithStatusCode {
|
func RelayImageHelper(c *gin.Context, relayMode int) *relaymodel.ErrorWithStatusCode {
|
||||||
ctx := c.Request.Context()
|
ctx := c.Request.Context()
|
||||||
meta := meta.GetByContext(c)
|
meta := metalib.GetByContext(c)
|
||||||
imageRequest, err := getImageRequest(c, meta.Mode)
|
imageRequest, err := getImageRequest(c, meta.Mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf(ctx, "getImageRequest failed: %s", err.Error())
|
logger.Errorf(ctx, "getImageRequest failed: %s", err.Error())
|
||||||
@ -114,7 +114,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
|
||||||
|
|
||||||
// model validation
|
// model validation
|
||||||
@ -130,7 +131,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
|
||||||
|
@ -17,13 +17,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"
|
||||||
"github.com/songquanpeng/one-api/relay/model"
|
relaymodel "github.com/songquanpeng/one-api/relay/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RelayTextHelper(c *gin.Context) *model.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 {
|
||||||
@ -34,7 +34,7 @@ func RelayTextHelper(c *gin.Context) *model.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)
|
||||||
@ -86,9 +86,12 @@ func RelayTextHelper(c *gin.Context) *model.ErrorWithStatusCode {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRequestBody(c *gin.Context, meta *meta.Meta, textRequest *model.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 && meta.APIType == apitype.OpenAI && meta.OriginModelName == meta.ActualModelName && meta.ChannelType != channeltype.Baichuan {
|
if !config.EnforceIncludeUsage &&
|
||||||
// no need to convert request for openai
|
meta.APIType == apitype.OpenAI &&
|
||||||
|
meta.OriginModelName == meta.ActualModelName &&
|
||||||
|
meta.ChannelType != channeltype.OpenAI && // openai also need to convert request
|
||||||
|
meta.ChannelType != channeltype.Baichuan {
|
||||||
return c.Request.Body, nil
|
return c.Request.Body, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
package meta
|
package meta
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/songquanpeng/one-api/common/ctxkey"
|
"github.com/songquanpeng/one-api/common/ctxkey"
|
||||||
"github.com/songquanpeng/one-api/model"
|
"github.com/songquanpeng/one-api/model"
|
||||||
"github.com/songquanpeng/one-api/relay/channeltype"
|
"github.com/songquanpeng/one-api/relay/channeltype"
|
||||||
"github.com/songquanpeng/one-api/relay/relaymode"
|
"github.com/songquanpeng/one-api/relay/relaymode"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Meta struct {
|
type Meta struct {
|
||||||
@ -33,6 +34,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 {
|
||||||
meta := Meta{
|
meta := Meta{
|
||||||
Mode: relaymode.GetByPath(c.Request.URL.Path),
|
Mode: relaymode.GetByPath(c.Request.URL.Path),
|
||||||
@ -44,6 +59,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(),
|
||||||
@ -57,5 +73,13 @@ func GetByContext(c *gin.Context) *Meta {
|
|||||||
meta.BaseURL = channeltype.ChannelBaseURLs[meta.ChannelType]
|
meta.BaseURL = channeltype.ChannelBaseURLs[meta.ChannelType]
|
||||||
}
|
}
|
||||||
meta.APIType = channeltype.ToAPIType(meta.ChannelType)
|
meta.APIType = channeltype.ToAPIType(meta.ChannelType)
|
||||||
|
|
||||||
|
meta.ActualModelName = GetMappedModelName(meta.OriginModelName, meta.ModelMapping)
|
||||||
|
|
||||||
|
Set2Context(c, &meta)
|
||||||
return &meta
|
return &meta
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Set2Context(c *gin.Context, meta *Meta) {
|
||||||
|
c.Set(ctxkey.Meta, meta)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user