chore: remove helper & util subpackage for relay
This commit is contained in:
parent
24ed170e7b
commit
f586ae0ad8
@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/songquanpeng/one-api/model"
|
"github.com/songquanpeng/one-api/model"
|
||||||
"github.com/songquanpeng/one-api/monitor"
|
"github.com/songquanpeng/one-api/monitor"
|
||||||
"github.com/songquanpeng/one-api/relay/channeltype"
|
"github.com/songquanpeng/one-api/relay/channeltype"
|
||||||
"github.com/songquanpeng/one-api/relay/util"
|
"github.com/songquanpeng/one-api/relay/client"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -97,7 +97,7 @@ func GetResponseBody(method, url string, channel *model.Channel, headers http.He
|
|||||||
for k := range headers {
|
for k := range headers {
|
||||||
req.Header.Add(k, headers.Get(k))
|
req.Header.Add(k, headers.Get(k))
|
||||||
}
|
}
|
||||||
res, err := util.HTTPClient.Do(req)
|
res, err := client.HTTPClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -12,12 +12,12 @@ import (
|
|||||||
"github.com/songquanpeng/one-api/middleware"
|
"github.com/songquanpeng/one-api/middleware"
|
||||||
"github.com/songquanpeng/one-api/model"
|
"github.com/songquanpeng/one-api/model"
|
||||||
"github.com/songquanpeng/one-api/monitor"
|
"github.com/songquanpeng/one-api/monitor"
|
||||||
|
relay "github.com/songquanpeng/one-api/relay"
|
||||||
"github.com/songquanpeng/one-api/relay/channeltype"
|
"github.com/songquanpeng/one-api/relay/channeltype"
|
||||||
"github.com/songquanpeng/one-api/relay/helper"
|
"github.com/songquanpeng/one-api/relay/controller"
|
||||||
"github.com/songquanpeng/one-api/relay/meta"
|
"github.com/songquanpeng/one-api/relay/meta"
|
||||||
relaymodel "github.com/songquanpeng/one-api/relay/model"
|
relaymodel "github.com/songquanpeng/one-api/relay/model"
|
||||||
"github.com/songquanpeng/one-api/relay/relaymode"
|
"github.com/songquanpeng/one-api/relay/relaymode"
|
||||||
"github.com/songquanpeng/one-api/relay/util"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
@ -60,7 +60,7 @@ func testChannel(channel *model.Channel) (err error, openaiErr *relaymodel.Error
|
|||||||
middleware.SetupContextForSelectedChannel(c, channel, "")
|
middleware.SetupContextForSelectedChannel(c, channel, "")
|
||||||
meta := meta.GetByContext(c)
|
meta := meta.GetByContext(c)
|
||||||
apiType := channeltype.ToAPIType(channel.Type)
|
apiType := channeltype.ToAPIType(channel.Type)
|
||||||
adaptor := helper.GetAdaptor(apiType)
|
adaptor := relay.GetAdaptor(apiType)
|
||||||
if adaptor == nil {
|
if adaptor == nil {
|
||||||
return fmt.Errorf("invalid api type: %d, adaptor is nil", apiType), nil
|
return fmt.Errorf("invalid api type: %d, adaptor is nil", apiType), nil
|
||||||
}
|
}
|
||||||
@ -90,7 +90,7 @@ func testChannel(channel *model.Channel) (err error, openaiErr *relaymodel.Error
|
|||||||
return err, nil
|
return err, nil
|
||||||
}
|
}
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
err := util.RelayErrorHandler(resp)
|
err := controller.RelayErrorHandler(resp)
|
||||||
return fmt.Errorf("status code %d: %s", resp.StatusCode, err.Error.Message), &err.Error
|
return fmt.Errorf("status code %d: %s", resp.StatusCode, err.Error.Message), &err.Error
|
||||||
}
|
}
|
||||||
usage, respErr := adaptor.DoResponse(c, resp, meta)
|
usage, respErr := adaptor.DoResponse(c, resp, meta)
|
||||||
@ -186,10 +186,10 @@ func testChannels(notify bool, scope string) error {
|
|||||||
_ = message.Notify(message.ByAll, fmt.Sprintf("渠道 %s (%d)测试超时", channel.Name, channel.Id), "", err.Error())
|
_ = message.Notify(message.ByAll, fmt.Sprintf("渠道 %s (%d)测试超时", channel.Name, channel.Id), "", err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if isChannelEnabled && util.ShouldDisableChannel(openaiErr, -1) {
|
if isChannelEnabled && monitor.ShouldDisableChannel(openaiErr, -1) {
|
||||||
monitor.DisableChannel(channel.Id, channel.Name, err.Error())
|
monitor.DisableChannel(channel.Id, channel.Name, err.Error())
|
||||||
}
|
}
|
||||||
if !isChannelEnabled && util.ShouldEnableChannel(err, openaiErr) {
|
if !isChannelEnabled && monitor.ShouldEnableChannel(err, openaiErr) {
|
||||||
monitor.EnableChannel(channel.Id, channel.Name)
|
monitor.EnableChannel(channel.Id, channel.Name)
|
||||||
}
|
}
|
||||||
channel.UpdateResponseTime(milliseconds)
|
channel.UpdateResponseTime(milliseconds)
|
||||||
|
@ -4,10 +4,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/songquanpeng/one-api/model"
|
"github.com/songquanpeng/one-api/model"
|
||||||
|
relay "github.com/songquanpeng/one-api/relay"
|
||||||
"github.com/songquanpeng/one-api/relay/adaptor/openai"
|
"github.com/songquanpeng/one-api/relay/adaptor/openai"
|
||||||
"github.com/songquanpeng/one-api/relay/apitype"
|
"github.com/songquanpeng/one-api/relay/apitype"
|
||||||
"github.com/songquanpeng/one-api/relay/channeltype"
|
"github.com/songquanpeng/one-api/relay/channeltype"
|
||||||
"github.com/songquanpeng/one-api/relay/helper"
|
|
||||||
"github.com/songquanpeng/one-api/relay/meta"
|
"github.com/songquanpeng/one-api/relay/meta"
|
||||||
relaymodel "github.com/songquanpeng/one-api/relay/model"
|
relaymodel "github.com/songquanpeng/one-api/relay/model"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -66,7 +66,7 @@ func init() {
|
|||||||
if i == apitype.AIProxyLibrary {
|
if i == apitype.AIProxyLibrary {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
adaptor := helper.GetAdaptor(i)
|
adaptor := relay.GetAdaptor(i)
|
||||||
channelName := adaptor.GetChannelName()
|
channelName := adaptor.GetChannelName()
|
||||||
modelNames := adaptor.GetModelList()
|
modelNames := adaptor.GetModelList()
|
||||||
for _, modelName := range modelNames {
|
for _, modelName := range modelNames {
|
||||||
@ -104,7 +104,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
channelId2Models = make(map[int][]string)
|
channelId2Models = make(map[int][]string)
|
||||||
for i := 1; i < channeltype.Dummy; i++ {
|
for i := 1; i < channeltype.Dummy; i++ {
|
||||||
adaptor := helper.GetAdaptor(channeltype.ToAPIType(i))
|
adaptor := relay.GetAdaptor(channeltype.ToAPIType(i))
|
||||||
meta := &meta.Meta{
|
meta := &meta.Meta{
|
||||||
ChannelType: i,
|
ChannelType: i,
|
||||||
}
|
}
|
||||||
|
@ -15,14 +15,13 @@ import (
|
|||||||
"github.com/songquanpeng/one-api/relay/controller"
|
"github.com/songquanpeng/one-api/relay/controller"
|
||||||
"github.com/songquanpeng/one-api/relay/model"
|
"github.com/songquanpeng/one-api/relay/model"
|
||||||
"github.com/songquanpeng/one-api/relay/relaymode"
|
"github.com/songquanpeng/one-api/relay/relaymode"
|
||||||
"github.com/songquanpeng/one-api/relay/util"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
// https://platform.openai.com/docs/api-reference/chat
|
// https://platform.openai.com/docs/api-reference/chat
|
||||||
|
|
||||||
func relay(c *gin.Context, relayMode int) *model.ErrorWithStatusCode {
|
func relayHelper(c *gin.Context, relayMode int) *model.ErrorWithStatusCode {
|
||||||
var err *model.ErrorWithStatusCode
|
var err *model.ErrorWithStatusCode
|
||||||
switch relayMode {
|
switch relayMode {
|
||||||
case relaymode.ImagesGenerations:
|
case relaymode.ImagesGenerations:
|
||||||
@ -47,7 +46,7 @@ func Relay(c *gin.Context) {
|
|||||||
logger.Debugf(ctx, "request body: %s", string(requestBody))
|
logger.Debugf(ctx, "request body: %s", string(requestBody))
|
||||||
}
|
}
|
||||||
channelId := c.GetInt("channel_id")
|
channelId := c.GetInt("channel_id")
|
||||||
bizErr := relay(c, relayMode)
|
bizErr := relayHelper(c, relayMode)
|
||||||
if bizErr == nil {
|
if bizErr == nil {
|
||||||
monitor.Emit(channelId, true)
|
monitor.Emit(channelId, true)
|
||||||
return
|
return
|
||||||
@ -76,7 +75,7 @@ func Relay(c *gin.Context) {
|
|||||||
middleware.SetupContextForSelectedChannel(c, channel, originalModel)
|
middleware.SetupContextForSelectedChannel(c, channel, originalModel)
|
||||||
requestBody, err := common.GetRequestBody(c)
|
requestBody, err := common.GetRequestBody(c)
|
||||||
c.Request.Body = io.NopCloser(bytes.NewBuffer(requestBody))
|
c.Request.Body = io.NopCloser(bytes.NewBuffer(requestBody))
|
||||||
bizErr = relay(c, relayMode)
|
bizErr = relayHelper(c, relayMode)
|
||||||
if bizErr == nil {
|
if bizErr == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -118,7 +117,7 @@ func shouldRetry(c *gin.Context, statusCode int) bool {
|
|||||||
func processChannelRelayError(ctx context.Context, channelId int, channelName string, err *model.ErrorWithStatusCode) {
|
func processChannelRelayError(ctx context.Context, channelId int, channelName string, err *model.ErrorWithStatusCode) {
|
||||||
logger.Errorf(ctx, "relay error (channel #%d): %s", channelId, err.Message)
|
logger.Errorf(ctx, "relay error (channel #%d): %s", channelId, err.Message)
|
||||||
// https://platform.openai.com/docs/guides/error-codes/api-errors
|
// https://platform.openai.com/docs/guides/error-codes/api-errors
|
||||||
if util.ShouldDisableChannel(&err.Error, err.StatusCode) {
|
if monitor.ShouldDisableChannel(&err.Error, err.StatusCode) {
|
||||||
monitor.DisableChannel(channelId, channelName, err.Message)
|
monitor.DisableChannel(channelId, channelName, err.Message)
|
||||||
} else {
|
} else {
|
||||||
monitor.Emit(channelId, false)
|
monitor.Emit(channelId, false)
|
||||||
|
62
monitor/manage.go
Normal file
62
monitor/manage.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package monitor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/songquanpeng/one-api/common/config"
|
||||||
|
"github.com/songquanpeng/one-api/relay/model"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ShouldDisableChannel(err *model.Error, statusCode int) bool {
|
||||||
|
if !config.AutomaticDisableChannelEnabled {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if statusCode == http.StatusUnauthorized {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
switch err.Type {
|
||||||
|
case "insufficient_quota":
|
||||||
|
return true
|
||||||
|
// https://docs.anthropic.com/claude/reference/errors
|
||||||
|
case "authentication_error":
|
||||||
|
return true
|
||||||
|
case "permission_error":
|
||||||
|
return true
|
||||||
|
case "forbidden":
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if err.Code == "invalid_api_key" || err.Code == "account_deactivated" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(err.Message, "Your credit balance is too low") { // anthropic
|
||||||
|
return true
|
||||||
|
} else if strings.HasPrefix(err.Message, "This organization has been disabled.") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
//if strings.Contains(err.Message, "quota") {
|
||||||
|
// return true
|
||||||
|
//}
|
||||||
|
if strings.Contains(err.Message, "credit") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if strings.Contains(err.Message, "balance") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func ShouldEnableChannel(err error, openAIErr *model.Error) bool {
|
||||||
|
if !config.AutomaticEnableChannelEnabled {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if openAIErr != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package helper
|
package relay
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/songquanpeng/one-api/relay/adaptor"
|
"github.com/songquanpeng/one-api/relay/adaptor"
|
@ -9,9 +9,9 @@ import (
|
|||||||
"github.com/songquanpeng/one-api/common"
|
"github.com/songquanpeng/one-api/common"
|
||||||
"github.com/songquanpeng/one-api/common/logger"
|
"github.com/songquanpeng/one-api/common/logger"
|
||||||
"github.com/songquanpeng/one-api/relay/adaptor/openai"
|
"github.com/songquanpeng/one-api/relay/adaptor/openai"
|
||||||
|
"github.com/songquanpeng/one-api/relay/client"
|
||||||
"github.com/songquanpeng/one-api/relay/constant"
|
"github.com/songquanpeng/one-api/relay/constant"
|
||||||
"github.com/songquanpeng/one-api/relay/model"
|
"github.com/songquanpeng/one-api/relay/model"
|
||||||
"github.com/songquanpeng/one-api/relay/util"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
@ -305,7 +305,7 @@ func getBaiduAccessTokenHelper(apiKey string) (*AccessToken, error) {
|
|||||||
}
|
}
|
||||||
req.Header.Add("Content-Type", "application/json")
|
req.Header.Add("Content-Type", "application/json")
|
||||||
req.Header.Add("Accept", "application/json")
|
req.Header.Add("Accept", "application/json")
|
||||||
res, err := util.ImpatientHTTPClient.Do(req)
|
res, err := client.ImpatientHTTPClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/songquanpeng/one-api/relay/client"
|
||||||
"github.com/songquanpeng/one-api/relay/meta"
|
"github.com/songquanpeng/one-api/relay/meta"
|
||||||
"github.com/songquanpeng/one-api/relay/util"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
@ -39,7 +39,7 @@ func DoRequestHelper(a Adaptor, c *gin.Context, meta *meta.Meta, requestBody io.
|
|||||||
}
|
}
|
||||||
|
|
||||||
func DoRequest(c *gin.Context, req *http.Request) (*http.Response, error) {
|
func DoRequest(c *gin.Context, req *http.Request) (*http.Response, error) {
|
||||||
resp, err := util.HTTPClient.Do(req)
|
resp, err := client.HTTPClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"github.com/songquanpeng/one-api/relay/meta"
|
"github.com/songquanpeng/one-api/relay/meta"
|
||||||
"github.com/songquanpeng/one-api/relay/model"
|
"github.com/songquanpeng/one-api/relay/model"
|
||||||
"github.com/songquanpeng/one-api/relay/relaymode"
|
"github.com/songquanpeng/one-api/relay/relaymode"
|
||||||
"github.com/songquanpeng/one-api/relay/util"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
@ -43,11 +42,11 @@ func (a *Adaptor) GetRequestURL(meta *meta.Meta) (string, error) {
|
|||||||
//https://github.com/songquanpeng/one-api/issues/1191
|
//https://github.com/songquanpeng/one-api/issues/1191
|
||||||
// {your endpoint}/openai/deployments/{your azure_model}/chat/completions?api-version={api_version}
|
// {your endpoint}/openai/deployments/{your azure_model}/chat/completions?api-version={api_version}
|
||||||
requestURL = fmt.Sprintf("/openai/deployments/%s/%s", model_, task)
|
requestURL = fmt.Sprintf("/openai/deployments/%s/%s", model_, task)
|
||||||
return util.GetFullRequestURL(meta.BaseURL, requestURL, meta.ChannelType), nil
|
return GetFullRequestURL(meta.BaseURL, requestURL, meta.ChannelType), nil
|
||||||
case channeltype.Minimax:
|
case channeltype.Minimax:
|
||||||
return minimax.GetRequestURL(meta)
|
return minimax.GetRequestURL(meta)
|
||||||
default:
|
default:
|
||||||
return util.GetFullRequestURL(meta.BaseURL, meta.RequestURLPath, meta.ChannelType), nil
|
return GetFullRequestURL(meta.BaseURL, meta.RequestURLPath, meta.ChannelType), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
package openai
|
package openai
|
||||||
|
|
||||||
import "github.com/songquanpeng/one-api/relay/model"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/songquanpeng/one-api/relay/channeltype"
|
||||||
|
"github.com/songquanpeng/one-api/relay/model"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
func ResponseText2Usage(responseText string, modeName string, promptTokens int) *model.Usage {
|
func ResponseText2Usage(responseText string, modeName string, promptTokens int) *model.Usage {
|
||||||
usage := &model.Usage{}
|
usage := &model.Usage{}
|
||||||
@ -9,3 +14,17 @@ func ResponseText2Usage(responseText string, modeName string, promptTokens int)
|
|||||||
usage.TotalTokens = usage.PromptTokens + usage.CompletionTokens
|
usage.TotalTokens = usage.PromptTokens + usage.CompletionTokens
|
||||||
return usage
|
return usage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetFullRequestURL(baseURL string, requestURL string, channelType int) string {
|
||||||
|
fullRequestURL := fmt.Sprintf("%s%s", baseURL, requestURL)
|
||||||
|
|
||||||
|
if strings.HasPrefix(baseURL, "https://gateway.ai.cloudflare.com") {
|
||||||
|
switch channelType {
|
||||||
|
case channeltype.OpenAI:
|
||||||
|
fullRequestURL = fmt.Sprintf("%s%s", baseURL, strings.TrimPrefix(requestURL, "/v1"))
|
||||||
|
case channeltype.Azure:
|
||||||
|
fullRequestURL = fmt.Sprintf("%s%s", baseURL, strings.TrimPrefix(requestURL, "/openai/deployments"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fullRequestURL
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package util
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/songquanpeng/one-api/common/config"
|
"github.com/songquanpeng/one-api/common/config"
|
@ -17,9 +17,9 @@ 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/client"
|
||||||
relaymodel "github.com/songquanpeng/one-api/relay/model"
|
relaymodel "github.com/songquanpeng/one-api/relay/model"
|
||||||
"github.com/songquanpeng/one-api/relay/relaymode"
|
"github.com/songquanpeng/one-api/relay/relaymode"
|
||||||
"github.com/songquanpeng/one-api/relay/util"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
@ -125,7 +125,7 @@ func RelayAudioHelper(c *gin.Context, relayMode int) *relaymodel.ErrorWithStatus
|
|||||||
baseURL = c.GetString("base_url")
|
baseURL = c.GetString("base_url")
|
||||||
}
|
}
|
||||||
|
|
||||||
fullRequestURL := util.GetFullRequestURL(baseURL, requestURL, channelType)
|
fullRequestURL := openai.GetFullRequestURL(baseURL, requestURL, channelType)
|
||||||
if channelType == channeltype.Azure {
|
if channelType == channeltype.Azure {
|
||||||
apiVersion := azure.GetAPIVersion(c)
|
apiVersion := azure.GetAPIVersion(c)
|
||||||
if relayMode == relaymode.AudioTranscription {
|
if relayMode == relaymode.AudioTranscription {
|
||||||
@ -162,7 +162,7 @@ func RelayAudioHelper(c *gin.Context, relayMode int) *relaymodel.ErrorWithStatus
|
|||||||
req.Header.Set("Content-Type", c.Request.Header.Get("Content-Type"))
|
req.Header.Set("Content-Type", c.Request.Header.Get("Content-Type"))
|
||||||
req.Header.Set("Accept", c.Request.Header.Get("Accept"))
|
req.Header.Set("Accept", c.Request.Header.Get("Accept"))
|
||||||
|
|
||||||
resp, err := util.HTTPClient.Do(req)
|
resp, err := client.HTTPClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return openai.ErrorWrapper(err, "do_request_failed", http.StatusInternalServerError)
|
return openai.ErrorWrapper(err, "do_request_failed", http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
@ -215,7 +215,7 @@ func RelayAudioHelper(c *gin.Context, relayMode int) *relaymodel.ErrorWithStatus
|
|||||||
resp.Body = io.NopCloser(bytes.NewBuffer(responseBody))
|
resp.Body = io.NopCloser(bytes.NewBuffer(responseBody))
|
||||||
}
|
}
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return util.RelayErrorHandler(resp)
|
return RelayErrorHandler(resp)
|
||||||
}
|
}
|
||||||
succeed = true
|
succeed = true
|
||||||
quotaDelta := quota - preConsumedQuota
|
quotaDelta := quota - preConsumedQuota
|
||||||
|
91
relay/controller/error.go
Normal file
91
relay/controller/error.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/songquanpeng/one-api/common/config"
|
||||||
|
"github.com/songquanpeng/one-api/common/logger"
|
||||||
|
"github.com/songquanpeng/one-api/relay/model"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GeneralErrorResponse struct {
|
||||||
|
Error model.Error `json:"error"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
Err string `json:"err"`
|
||||||
|
ErrorMsg string `json:"error_msg"`
|
||||||
|
Header struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
} `json:"header"`
|
||||||
|
Response struct {
|
||||||
|
Error struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
} `json:"error"`
|
||||||
|
} `json:"response"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e GeneralErrorResponse) ToMessage() string {
|
||||||
|
if e.Error.Message != "" {
|
||||||
|
return e.Error.Message
|
||||||
|
}
|
||||||
|
if e.Message != "" {
|
||||||
|
return e.Message
|
||||||
|
}
|
||||||
|
if e.Msg != "" {
|
||||||
|
return e.Msg
|
||||||
|
}
|
||||||
|
if e.Err != "" {
|
||||||
|
return e.Err
|
||||||
|
}
|
||||||
|
if e.ErrorMsg != "" {
|
||||||
|
return e.ErrorMsg
|
||||||
|
}
|
||||||
|
if e.Header.Message != "" {
|
||||||
|
return e.Header.Message
|
||||||
|
}
|
||||||
|
if e.Response.Error.Message != "" {
|
||||||
|
return e.Response.Error.Message
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func RelayErrorHandler(resp *http.Response) (ErrorWithStatusCode *model.ErrorWithStatusCode) {
|
||||||
|
ErrorWithStatusCode = &model.ErrorWithStatusCode{
|
||||||
|
StatusCode: resp.StatusCode,
|
||||||
|
Error: model.Error{
|
||||||
|
Message: "",
|
||||||
|
Type: "upstream_error",
|
||||||
|
Code: "bad_response_status_code",
|
||||||
|
Param: strconv.Itoa(resp.StatusCode),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
responseBody, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if config.DebugEnabled {
|
||||||
|
logger.SysLog(fmt.Sprintf("error happened, status code: %d, response: \n%s", resp.StatusCode, string(responseBody)))
|
||||||
|
}
|
||||||
|
err = resp.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var errResponse GeneralErrorResponse
|
||||||
|
err = json.Unmarshal(responseBody, &errResponse)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if errResponse.Error.Message != "" {
|
||||||
|
// OpenAI format error, so we override the default one
|
||||||
|
ErrorWithStatusCode.Error = errResponse.Error
|
||||||
|
} else {
|
||||||
|
ErrorWithStatusCode.Error.Message = errResponse.ToMessage()
|
||||||
|
}
|
||||||
|
if ErrorWithStatusCode.Error.Message == "" {
|
||||||
|
ErrorWithStatusCode.Error.Message = fmt.Sprintf("bad response status code %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
@ -12,10 +12,10 @@ 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/controller/validator"
|
||||||
"github.com/songquanpeng/one-api/relay/meta"
|
"github.com/songquanpeng/one-api/relay/meta"
|
||||||
relaymodel "github.com/songquanpeng/one-api/relay/model"
|
relaymodel "github.com/songquanpeng/one-api/relay/model"
|
||||||
"github.com/songquanpeng/one-api/relay/relaymode"
|
"github.com/songquanpeng/one-api/relay/relaymode"
|
||||||
"github.com/songquanpeng/one-api/relay/util"
|
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
@ -32,7 +32,7 @@ func getAndValidateTextRequest(c *gin.Context, relayMode int) (*relaymodel.Gener
|
|||||||
if relayMode == relaymode.Embeddings && textRequest.Model == "" {
|
if relayMode == relaymode.Embeddings && textRequest.Model == "" {
|
||||||
textRequest.Model = c.Param("model")
|
textRequest.Model = c.Param("model")
|
||||||
}
|
}
|
||||||
err = util.ValidateTextRequest(textRequest, relayMode)
|
err = validator.ValidateTextRequest(textRequest, relayMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -193,3 +193,14 @@ func postConsumeQuota(ctx context.Context, usage *relaymodel.Usage, meta *meta.M
|
|||||||
model.UpdateUserUsedQuotaAndRequestCount(meta.UserId, quota)
|
model.UpdateUserUsedQuotaAndRequestCount(meta.UserId, quota)
|
||||||
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
|
||||||
|
}
|
||||||
|
@ -9,13 +9,12 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/songquanpeng/one-api/common/logger"
|
"github.com/songquanpeng/one-api/common/logger"
|
||||||
"github.com/songquanpeng/one-api/model"
|
"github.com/songquanpeng/one-api/model"
|
||||||
|
"github.com/songquanpeng/one-api/relay"
|
||||||
"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/helper"
|
|
||||||
"github.com/songquanpeng/one-api/relay/meta"
|
"github.com/songquanpeng/one-api/relay/meta"
|
||||||
relaymodel "github.com/songquanpeng/one-api/relay/model"
|
relaymodel "github.com/songquanpeng/one-api/relay/model"
|
||||||
"github.com/songquanpeng/one-api/relay/util"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
@ -41,7 +40,7 @@ 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 = util.GetMappedModelName(imageRequest.Model, meta.ModelMapping)
|
imageRequest.Model, isModelMapped = getMappedModelName(imageRequest.Model, meta.ModelMapping)
|
||||||
meta.ActualModelName = imageRequest.Model
|
meta.ActualModelName = imageRequest.Model
|
||||||
|
|
||||||
// model validation
|
// model validation
|
||||||
@ -66,7 +65,7 @@ func RelayImageHelper(c *gin.Context, relayMode int) *relaymodel.ErrorWithStatus
|
|||||||
requestBody = c.Request.Body
|
requestBody = c.Request.Body
|
||||||
}
|
}
|
||||||
|
|
||||||
adaptor := helper.GetAdaptor(meta.APIType)
|
adaptor := relay.GetAdaptor(meta.APIType)
|
||||||
if adaptor == nil {
|
if adaptor == nil {
|
||||||
return openai.ErrorWrapper(fmt.Errorf("invalid api type: %d", meta.APIType), "invalid_api_type", http.StatusBadRequest)
|
return openai.ErrorWrapper(fmt.Errorf("invalid api type: %d", meta.APIType), "invalid_api_type", http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
@ -6,15 +6,14 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/songquanpeng/one-api/common/logger"
|
"github.com/songquanpeng/one-api/common/logger"
|
||||||
|
"github.com/songquanpeng/one-api/relay"
|
||||||
"github.com/songquanpeng/one-api/relay/adaptor/openai"
|
"github.com/songquanpeng/one-api/relay/adaptor/openai"
|
||||||
"github.com/songquanpeng/one-api/relay/apitype"
|
"github.com/songquanpeng/one-api/relay/apitype"
|
||||||
"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/helper"
|
|
||||||
"github.com/songquanpeng/one-api/relay/meta"
|
"github.com/songquanpeng/one-api/relay/meta"
|
||||||
"github.com/songquanpeng/one-api/relay/model"
|
"github.com/songquanpeng/one-api/relay/model"
|
||||||
"github.com/songquanpeng/one-api/relay/util"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
@ -34,7 +33,7 @@ func RelayTextHelper(c *gin.Context) *model.ErrorWithStatusCode {
|
|||||||
// map model name
|
// map model name
|
||||||
var isModelMapped bool
|
var isModelMapped bool
|
||||||
meta.OriginModelName = textRequest.Model
|
meta.OriginModelName = textRequest.Model
|
||||||
textRequest.Model, isModelMapped = util.GetMappedModelName(textRequest.Model, meta.ModelMapping)
|
textRequest.Model, isModelMapped = getMappedModelName(textRequest.Model, meta.ModelMapping)
|
||||||
meta.ActualModelName = textRequest.Model
|
meta.ActualModelName = textRequest.Model
|
||||||
// get model ratio & group ratio
|
// get model ratio & group ratio
|
||||||
modelRatio := billingratio.GetModelRatio(textRequest.Model)
|
modelRatio := billingratio.GetModelRatio(textRequest.Model)
|
||||||
@ -49,7 +48,7 @@ func RelayTextHelper(c *gin.Context) *model.ErrorWithStatusCode {
|
|||||||
return bizErr
|
return bizErr
|
||||||
}
|
}
|
||||||
|
|
||||||
adaptor := helper.GetAdaptor(meta.APIType)
|
adaptor := relay.GetAdaptor(meta.APIType)
|
||||||
if adaptor == nil {
|
if adaptor == nil {
|
||||||
return openai.ErrorWrapper(fmt.Errorf("invalid api type: %d", meta.APIType), "invalid_api_type", http.StatusBadRequest)
|
return openai.ErrorWrapper(fmt.Errorf("invalid api type: %d", meta.APIType), "invalid_api_type", http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
@ -90,7 +89,7 @@ func RelayTextHelper(c *gin.Context) *model.ErrorWithStatusCode {
|
|||||||
errorHappened := (resp.StatusCode != http.StatusOK) || (meta.IsStream && resp.Header.Get("Content-Type") == "application/json")
|
errorHappened := (resp.StatusCode != http.StatusOK) || (meta.IsStream && resp.Header.Get("Content-Type") == "application/json")
|
||||||
if errorHappened {
|
if errorHappened {
|
||||||
billing.ReturnPreConsumedQuota(ctx, preConsumedQuota, meta.TokenId)
|
billing.ReturnPreConsumedQuota(ctx, preConsumedQuota, meta.TokenId)
|
||||||
return util.RelayErrorHandler(resp)
|
return RelayErrorHandler(resp)
|
||||||
}
|
}
|
||||||
meta.IsStream = meta.IsStream || strings.HasPrefix(resp.Header.Get("Content-Type"), "text/event-stream")
|
meta.IsStream = meta.IsStream || strings.HasPrefix(resp.Header.Get("Content-Type"), "text/event-stream")
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package util
|
package validator
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
@ -1,161 +0,0 @@
|
|||||||
package util
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"github.com/songquanpeng/one-api/common/config"
|
|
||||||
"github.com/songquanpeng/one-api/common/logger"
|
|
||||||
"github.com/songquanpeng/one-api/relay/channeltype"
|
|
||||||
relaymodel "github.com/songquanpeng/one-api/relay/model"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ShouldDisableChannel(err *relaymodel.Error, statusCode int) bool {
|
|
||||||
if !config.AutomaticDisableChannelEnabled {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if statusCode == http.StatusUnauthorized {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
switch err.Type {
|
|
||||||
case "insufficient_quota":
|
|
||||||
return true
|
|
||||||
// https://docs.anthropic.com/claude/reference/errors
|
|
||||||
case "authentication_error":
|
|
||||||
return true
|
|
||||||
case "permission_error":
|
|
||||||
return true
|
|
||||||
case "forbidden":
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if err.Code == "invalid_api_key" || err.Code == "account_deactivated" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(err.Message, "Your credit balance is too low") { // anthropic
|
|
||||||
return true
|
|
||||||
} else if strings.HasPrefix(err.Message, "This organization has been disabled.") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
//if strings.Contains(err.Message, "quota") {
|
|
||||||
// return true
|
|
||||||
//}
|
|
||||||
if strings.Contains(err.Message, "credit") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if strings.Contains(err.Message, "balance") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func ShouldEnableChannel(err error, openAIErr *relaymodel.Error) bool {
|
|
||||||
if !config.AutomaticEnableChannelEnabled {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if openAIErr != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
type GeneralErrorResponse struct {
|
|
||||||
Error relaymodel.Error `json:"error"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
Msg string `json:"msg"`
|
|
||||||
Err string `json:"err"`
|
|
||||||
ErrorMsg string `json:"error_msg"`
|
|
||||||
Header struct {
|
|
||||||
Message string `json:"message"`
|
|
||||||
} `json:"header"`
|
|
||||||
Response struct {
|
|
||||||
Error struct {
|
|
||||||
Message string `json:"message"`
|
|
||||||
} `json:"error"`
|
|
||||||
} `json:"response"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e GeneralErrorResponse) ToMessage() string {
|
|
||||||
if e.Error.Message != "" {
|
|
||||||
return e.Error.Message
|
|
||||||
}
|
|
||||||
if e.Message != "" {
|
|
||||||
return e.Message
|
|
||||||
}
|
|
||||||
if e.Msg != "" {
|
|
||||||
return e.Msg
|
|
||||||
}
|
|
||||||
if e.Err != "" {
|
|
||||||
return e.Err
|
|
||||||
}
|
|
||||||
if e.ErrorMsg != "" {
|
|
||||||
return e.ErrorMsg
|
|
||||||
}
|
|
||||||
if e.Header.Message != "" {
|
|
||||||
return e.Header.Message
|
|
||||||
}
|
|
||||||
if e.Response.Error.Message != "" {
|
|
||||||
return e.Response.Error.Message
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func RelayErrorHandler(resp *http.Response) (ErrorWithStatusCode *relaymodel.ErrorWithStatusCode) {
|
|
||||||
ErrorWithStatusCode = &relaymodel.ErrorWithStatusCode{
|
|
||||||
StatusCode: resp.StatusCode,
|
|
||||||
Error: relaymodel.Error{
|
|
||||||
Message: "",
|
|
||||||
Type: "upstream_error",
|
|
||||||
Code: "bad_response_status_code",
|
|
||||||
Param: strconv.Itoa(resp.StatusCode),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
responseBody, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if config.DebugEnabled {
|
|
||||||
logger.SysLog(fmt.Sprintf("error happened, status code: %d, response: \n%s", resp.StatusCode, string(responseBody)))
|
|
||||||
}
|
|
||||||
err = resp.Body.Close()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var errResponse GeneralErrorResponse
|
|
||||||
err = json.Unmarshal(responseBody, &errResponse)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if errResponse.Error.Message != "" {
|
|
||||||
// OpenAI format error, so we override the default one
|
|
||||||
ErrorWithStatusCode.Error = errResponse.Error
|
|
||||||
} else {
|
|
||||||
ErrorWithStatusCode.Error.Message = errResponse.ToMessage()
|
|
||||||
}
|
|
||||||
if ErrorWithStatusCode.Error.Message == "" {
|
|
||||||
ErrorWithStatusCode.Error.Message = fmt.Sprintf("bad response status code %d", resp.StatusCode)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetFullRequestURL(baseURL string, requestURL string, channelType int) string {
|
|
||||||
fullRequestURL := fmt.Sprintf("%s%s", baseURL, requestURL)
|
|
||||||
|
|
||||||
if strings.HasPrefix(baseURL, "https://gateway.ai.cloudflare.com") {
|
|
||||||
switch channelType {
|
|
||||||
case channeltype.OpenAI:
|
|
||||||
fullRequestURL = fmt.Sprintf("%s%s", baseURL, strings.TrimPrefix(requestURL, "/v1"))
|
|
||||||
case channeltype.Azure:
|
|
||||||
fullRequestURL = fmt.Sprintf("%s%s", baseURL, strings.TrimPrefix(requestURL, "/openai/deployments"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fullRequestURL
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
package util
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user