Ver código fonte

feat: golangci-lint v2 (#225)

* feat: golangci-lint v2

* fix: ci lint

* fix: ci lint

* fix: ci lint
zijiren 7 meses atrás
pai
commit
98f6405183
100 arquivos alterados com 2412 adições e 660 exclusões
  1. 15 15
      .github/workflows/ci.yml
  2. 109 57
      core/.golangci.yml
  3. 12 3
      core/common/balance/balance.go
  4. 9 2
      core/common/balance/mock.go
  5. 49 15
      core/common/balance/sealos.go
  6. 1 1
      core/common/env/helper.go
  7. 12 2
      core/common/fastJSONSerializer/fastJSONSerializer.go
  8. 3 4
      core/common/image/image.go
  9. 6 1
      core/common/mcpproxy/sse.go
  10. 20 18
      core/common/mcpproxy/sse_test.go
  11. 9 3
      core/common/mcpproxy/streamable.go
  12. 1 1
      core/common/network/ip.go
  13. 6 1
      core/common/notify/feishu.go
  14. 7 1
      core/common/notify/notify.go
  15. 6 1
      core/common/notify/std.go
  16. 1 1
      core/common/redis.go
  17. 2 1
      core/common/render/event.go
  18. 128 20
      core/common/reqlimit/main.go
  19. 12 4
      core/common/reqlimit/mem.go
  20. 5 1
      core/common/reqlimit/mem_test.go
  21. 12 2
      core/common/reqlimit/redis.go
  22. 9 6
      core/common/splitter/splitter.go
  23. 45 6
      core/controller/channel-billing.go
  24. 62 9
      core/controller/channel-test.go
  25. 36 4
      core/controller/channel.go
  26. 61 10
      core/controller/dashboard.go
  27. 23 10
      core/controller/embedmcp.go
  28. 8 1
      core/controller/groupmcp.go
  29. 6 2
      core/controller/import.go
  30. 7 1
      core/controller/modelconfig.go
  31. 17 5
      core/controller/publicmcp-server.go
  32. 7 1
      core/controller/publicmcp.go
  33. 100 20
      core/controller/relay-controller.go
  34. 5 1
      core/controller/relay-dashboard.go
  35. 0 1
      core/controller/relay.go
  36. 20 2
      core/controller/token.go
  37. 25 5
      core/main.go
  38. 20 4
      core/middleware/auth.go
  39. 113 24
      core/middleware/distributor.go
  40. 17 5
      core/middleware/distributor_test.go
  41. 14 3
      core/middleware/log.go
  42. 21 3
      core/middleware/utils.go
  43. 39 5
      core/model/batch.go
  44. 38 14
      core/model/cache.go
  45. 45 13
      core/model/channel.go
  46. 3 3
      core/model/channeltest.go
  47. 16 6
      core/model/consumeerr.go
  48. 33 16
      core/model/group.go
  49. 14 6
      core/model/groupmcp.go
  50. 5 3
      core/model/groupmodel.go
  51. 7 2
      core/model/groupsummary.go
  52. 40 25
      core/model/log.go
  53. 19 5
      core/model/main.go
  54. 17 9
      core/model/modelconfig.go
  55. 24 8
      core/model/option.go
  56. 24 10
      core/model/publicmcp.go
  57. 13 11
      core/model/retrylog.go
  58. 83 17
      core/model/summary.go
  59. 33 16
      core/model/token.go
  60. 1 1
      core/model/utils.go
  61. 32 7
      core/monitor/memmodel.go
  62. 23 4
      core/monitor/model.go
  63. 29 6
      core/relay/adaptor/ali/adaptor.go
  64. 3 1
      core/relay/adaptor/ali/constants.go
  65. 32 11
      core/relay/adaptor/ali/embeddings.go
  66. 47 12
      core/relay/adaptor/ali/image.go
  67. 24 5
      core/relay/adaptor/ali/rerank.go
  68. 57 13
      core/relay/adaptor/ali/stt-realtime.go
  69. 29 6
      core/relay/adaptor/ali/tts.go
  70. 19 4
      core/relay/adaptor/anthropic/adaptor.go
  71. 8 2
      core/relay/adaptor/anthropic/error.go
  72. 2 1
      core/relay/adaptor/anthropic/event.go
  73. 22 5
      core/relay/adaptor/anthropic/main.go
  74. 39 11
      core/relay/adaptor/anthropic/openai.go
  75. 20 4
      core/relay/adaptor/aws/adaptor.go
  76. 8 2
      core/relay/adaptor/aws/claude/adapter.go
  77. 78 18
      core/relay/adaptor/aws/claude/main.go
  78. 8 2
      core/relay/adaptor/aws/llama3/adapter.go
  79. 58 12
      core/relay/adaptor/aws/llama3/main.go
  80. 10 3
      core/relay/adaptor/aws/utils/adaptor.go
  81. 36 6
      core/relay/adaptor/azure/main.go
  82. 19 4
      core/relay/adaptor/baidu/adaptor.go
  83. 25 5
      core/relay/adaptor/baidu/embeddings.go
  84. 5 1
      core/relay/adaptor/baidu/error.go
  85. 16 4
      core/relay/adaptor/baidu/image.go
  86. 21 6
      core/relay/adaptor/baidu/main.go
  87. 25 5
      core/relay/adaptor/baidu/rerank.go
  88. 19 6
      core/relay/adaptor/baidu/token.go
  89. 14 3
      core/relay/adaptor/baiduv2/adaptor.go
  90. 17 3
      core/relay/adaptor/baiduv2/token.go
  91. 2 1
      core/relay/adaptor/cloudflare/adaptor.go
  92. 14 3
      core/relay/adaptor/cohere/adaptor.go
  93. 28 8
      core/relay/adaptor/cohere/main.go
  94. 14 3
      core/relay/adaptor/coze/adaptor.go
  95. 30 7
      core/relay/adaptor/coze/main.go
  96. 19 4
      core/relay/adaptor/doc2x/adaptor.go
  97. 2 1
      core/relay/adaptor/doc2x/html2md_test.go
  98. 40 9
      core/relay/adaptor/doc2x/pdf.go
  99. 9 2
      core/relay/adaptor/doubao/main.go
  100. 14 3
      core/relay/adaptor/doubaoaudio/main.go

+ 15 - 15
.github/workflows/ci.yml

@@ -37,19 +37,19 @@ jobs:
           go test -v -timeout 30s -count=1 ./...
 
       - name: Run Linter
-        uses: golangci/golangci-lint-action@v6
+        uses: golangci/golangci-lint-action@v8
         with:
-          version: v1.64.8
+          version: latest
           working-directory: core
-          args: "--color always --out-format colored-line-number --max-issues-per-linter 0 --max-same-issues 0 --config ./.golangci.yml"
+          args: --color always
 
       - name: Run Fix Linter
-        uses: golangci/golangci-lint-action@v6
+        uses: golangci/golangci-lint-action@v8
         if: ${{ failure() }}
         with:
           install-mode: none
           working-directory: core
-          args: "--fix --color always --out-format colored-line-number --max-issues-per-linter 0 --max-same-issues 0 --config ./.golangci.yml"
+          args: --fix --color always
 
       - name: Auto Fix Diff Content
         if: ${{ failure() }}
@@ -75,19 +75,19 @@ jobs:
           go test -v -timeout 30s -count=1 ./...
 
       - name: Run Linter
-        uses: golangci/golangci-lint-action@v6
+        uses: golangci/golangci-lint-action@v8
         with:
-          version: v1.64.8
+          version: latest
           working-directory: mcp-servers
-          args: "--color always --out-format colored-line-number --max-issues-per-linter 0 --max-same-issues 0 --config ./.golangci.yml"
+          args: --color always
 
       - name: Run Fix Linter
-        uses: golangci/golangci-lint-action@v6
+        uses: golangci/golangci-lint-action@v8
         if: ${{ failure() }}
         with:
           install-mode: none
           working-directory: mcp-servers
-          args: "--fix --color always --out-format colored-line-number --max-issues-per-linter 0 --max-same-issues 0 --config ./.golangci.yml"
+          args: --fix --color always
 
       - name: Auto Fix Diff Content
         if: ${{ failure() }}
@@ -108,19 +108,19 @@ jobs:
           go-version-file: "openapi-mcp/go.mod"
 
       - name: Run Linter
-        uses: golangci/golangci-lint-action@v6
+        uses: golangci/golangci-lint-action@v8
         with:
-          version: v1.64.8
+          version: latest
           working-directory: openapi-mcp
-          args: "--color always --out-format colored-line-number --max-issues-per-linter 0 --max-same-issues 0 --config ./.golangci.yml"
+          args: --color always
 
       - name: Run Fix Linter
-        uses: golangci/golangci-lint-action@v6
+        uses: golangci/golangci-lint-action@v8
         if: ${{ failure() }}
         with:
           install-mode: none
           working-directory: openapi-mcp
-          args: "--fix --color always --out-format colored-line-number --max-issues-per-linter 0 --max-same-issues 0 --config ./.golangci.yml"
+          args: --fix --color always
 
       - name: Auto Fix Diff Content
         if: ${{ failure() }}

+ 109 - 57
core/.golangci.yml

@@ -1,78 +1,130 @@
+version: "2"
+
 run:
-  go: "1.23"
-  timeout: 5m
-  tests: true
+  go: "1.24"
+  relative-path-mode: gomod
   modules-download-mode: readonly
 
 issues:
-  exclude-dirs-use-default: true
+  max-issues-per-linter: 0
+  max-same-issues: 0
 
 linters:
-  disable-all: true
+  default: none
   enable:
-    - gofmt
-    - goimports
-    - revive
-    - stylecheck
-    - gosimple
-    - misspell
-    - whitespace
-    - ineffassign
-    - typecheck
-    - errcheck
-    - govet
-    - staticcheck
-    - unused
-    - unconvert
-    - nosprintfhostport
-    - gofumpt
-    - sloglint
-    - unparam
-    - usestdlibvars
-    - usetesting
-    - gocritic
+    - asasalint
     - asciicheck
     - bidichk
-    - gocheckcompilerdirectives
-    - asasalint
+    - bodyclose
+    - canonicalheader
+    - containedctx
+    - copyloopvar
     - durationcheck
-    - exptostd
-    - forbidigo
-    - mirror
-    - errorlint
+    - errcheck
     - errchkjson
     - errname
-    - gosec
-    - sqlclosecheck
-    - rowserrcheck
-    - noctx
-    - bodyclose
-    - prealloc
-    - perfsprint
-    - unconvert
+    - errorlint
+    - exptostd
+    - fatcontext
+    - forbidigo
+    - ginkgolinter
+    - gocheckcompilerdirectives
+    - gocritic
     - gocyclo
-    - testifylint
-    - thelper
-    - testpackage
-    - tparallel
-    - goheader
-    - gomodguard
-    - musttag
-    - tagalign
+    - goprintffuncname
+    - gosec
+    - govet
     - iface
     - importas
-    - loggercheck
-    - canonicalheader
-    - containedctx
-    - copyloopvar
+    - inamedparam
+    - ineffassign
     - intrange
-    - makezero
+    - loggercheck
+    - mirror
+    - misspell
+    - musttag
+    - nakedret
+    - noctx
     - nolintlint
+    - nosprintfhostport
+    - perfsprint
+    - prealloc
     - predeclared
-    - wastedassign
-    - ginkgolinter
-    - goprintffuncname
-    - inamedparam
     - promlinter
     - protogetter
     - reassign
+    - revive
+    - rowserrcheck
+    - sloglint
+    - spancheck
+    - sqlclosecheck
+    - staticcheck
+    - testpackage
+    - thelper
+    - tparallel
+    - unconvert
+    - unparam
+    - unused
+    - usestdlibvars
+    - usetesting
+    - wastedassign
+    - whitespace
+  exclusions:
+    generated: lax
+    presets:
+      - comments
+      - common-false-positives
+      - legacy
+      - std-error-handling
+    paths:
+      - third_party$
+      - builtin$
+      - examples$
+  settings:
+    copyloopvar:
+      check-alias: true
+    cyclop:
+      max-complexity: 15
+    errcheck:
+      check-type-assertions: true
+    forbidigo:
+      analyze-types: true
+    prealloc:
+      for-loops: true
+    staticcheck:
+      dot-import-whitelist: []
+      http-status-code-whitelist: []
+    usestdlibvars:
+      time-month: true
+      time-layout: true
+      crypto-hash: true
+      default-rpc-path: true
+      sql-isolation-level: true
+      tls-signature-scheme: true
+      constant-kind: true
+    usetesting:
+      os-temp-dir: true
+
+formatters:
+  enable:
+    - gci
+    - gofmt
+    - gofumpt
+    - golines
+  exclusions:
+    generated: lax
+    paths:
+      - third_party$
+      - builtin$
+      - examples$
+  settings:
+    gofmt:
+      rewrite-rules:
+        - pattern: "interface{}"
+          replacement: "any"
+        - pattern: "a[b:len(a)]"
+          replacement: "a[b:]"
+    gofumpt:
+      extra-rules: true
+    golines:
+      shorten-comments: true

+ 12 - 3
core/common/balance/balance.go

@@ -7,7 +7,10 @@ import (
 )
 
 type GroupBalance interface {
-	GetGroupRemainBalance(ctx context.Context, group model.GroupCache) (float64, PostGroupConsumer, error)
+	GetGroupRemainBalance(
+		ctx context.Context,
+		group model.GroupCache,
+	) (float64, PostGroupConsumer, error)
 }
 
 type PostGroupConsumer interface {
@@ -19,10 +22,16 @@ var (
 	Default              = mock
 )
 
-func MockGetGroupRemainBalance(ctx context.Context, group model.GroupCache) (float64, PostGroupConsumer, error) {
+func MockGetGroupRemainBalance(
+	ctx context.Context,
+	group model.GroupCache,
+) (float64, PostGroupConsumer, error) {
 	return mock.GetGroupRemainBalance(ctx, group)
 }
 
-func GetGroupRemainBalance(ctx context.Context, group model.GroupCache) (float64, PostGroupConsumer, error) {
+func GetGroupRemainBalance(
+	ctx context.Context,
+	group model.GroupCache,
+) (float64, PostGroupConsumer, error) {
 	return Default.GetGroupRemainBalance(ctx, group)
 }

+ 9 - 2
core/common/balance/mock.go

@@ -18,10 +18,17 @@ func NewMockGroupBalance() *MockGroupBalance {
 	return &MockGroupBalance{}
 }
 
-func (q *MockGroupBalance) GetGroupRemainBalance(_ context.Context, _ model.GroupCache) (float64, PostGroupConsumer, error) {
+func (q *MockGroupBalance) GetGroupRemainBalance(
+	_ context.Context,
+	_ model.GroupCache,
+) (float64, PostGroupConsumer, error) {
 	return mockBalance, q, nil
 }
 
-func (q *MockGroupBalance) PostGroupConsume(_ context.Context, _ string, usage float64) (float64, error) {
+func (q *MockGroupBalance) PostGroupConsume(
+	_ context.Context,
+	_ string,
+	usage float64,
+) (float64, error) {
 	return usage, nil
 }

+ 49 - 15
core/common/balance/sealos.go

@@ -42,7 +42,10 @@ var (
 
 var (
 	sealosCheckRealNameEnable       = env.Bool("BALANCE_SEALOS_CHECK_REAL_NAME_ENABLE", false)
-	sealosNoRealNameUsedAmountLimit = env.Float64("BALANCE_SEALOS_NO_REAL_NAME_USED_AMOUNT_LIMIT", 1)
+	sealosNoRealNameUsedAmountLimit = env.Float64(
+		"BALANCE_SEALOS_NO_REAL_NAME_USED_AMOUNT_LIMIT",
+		1,
+	)
 )
 
 type Sealos struct {
@@ -50,7 +53,7 @@ type Sealos struct {
 }
 
 // FIXME: 如果获取余额能成功,但是消费永远失败,需要加一个失败次数限制,如果失败次数超过一定阈值,暂停服务
-func InitSealos(jwtKey string, accountURL string) error {
+func InitSealos(jwtKey, accountURL string) error {
 	token, err := newSealosToken(jwtKey)
 	if err != nil {
 		return fmt.Errorf("failed to generate sealos jwt token: %w", err)
@@ -148,13 +151,16 @@ func cacheDecreaseGroupBalance(ctx context.Context, group string, amount int64)
 	if !common.RedisEnabled || !sealosRedisCacheEnable {
 		return nil
 	}
-	return decreaseGroupBalanceScript.Run(ctx, common.RDB, []string{fmt.Sprintf(sealosGroupBalanceKey, group)}, amount).Err()
+	return decreaseGroupBalanceScript.Run(ctx, common.RDB, []string{fmt.Sprintf(sealosGroupBalanceKey, group)}, amount).
+		Err()
 }
 
 var ErrNoRealNameUsedAmountLimit = errors.New("达到未实名用户使用额度限制,请实名认证")
 
-func (s *Sealos) GetGroupRemainBalance(ctx context.Context, group model.GroupCache) (float64, PostGroupConsumer, error) {
-	var errs []error
+func (s *Sealos) GetGroupRemainBalance(
+	ctx context.Context,
+	group model.GroupCache,
+) (float64, PostGroupConsumer, error) {
 	for i := 0; ; i++ {
 		balance, userUID, err := s.getGroupRemainBalance(ctx, group.ID)
 		if err == nil {
@@ -166,9 +172,8 @@ func (s *Sealos) GetGroupRemainBalance(ctx context.Context, group model.GroupCac
 			return decimal.NewFromInt(balance).Div(decimalBalancePrecision).InexactFloat64(),
 				newSealosPostGroupConsumer(s.accountURL, group.ID, userUID), nil
 		}
-		errs = append(errs, err)
 		if i == getBalanceRetry-1 {
-			return 0, nil, errors.Join(errs...)
+			return 0, nil, err
 		}
 		time.Sleep(time.Second)
 	}
@@ -199,7 +204,8 @@ func cacheSetUserRealName(ctx context.Context, userUID string, realName bool) er
 	} else {
 		expireTime = time.Minute * 1
 	}
-	return common.RDB.Set(ctx, fmt.Sprintf(sealosUserRealNameKey, userUID), realName, expireTime).Err()
+	return common.RDB.Set(ctx, fmt.Sprintf(sealosUserRealNameKey, userUID), realName, expireTime).
+		Err()
 }
 
 func (s *Sealos) checkRealName(ctx context.Context, userUID string) bool {
@@ -249,7 +255,12 @@ func (s *Sealos) fetchRealNameFromAPI(ctx context.Context, userUID string) (bool
 	}
 
 	if resp.StatusCode != http.StatusOK || sealosResp.Error != "" {
-		return false, fmt.Errorf("get user (%s) real name failed with status code %d, error: %s", userUID, resp.StatusCode, sealosResp.Error)
+		return false, fmt.Errorf(
+			"get user (%s) real name failed with status code %d, error: %s",
+			userUID,
+			resp.StatusCode,
+			sealosResp.Error,
+		)
 	}
 
 	return sealosResp.IsRealName, nil
@@ -275,11 +286,22 @@ func (s *Sealos) getGroupRemainBalance(ctx context.Context, group string) (int64
 	return balance, userUID, nil
 }
 
-func (s *Sealos) fetchBalanceFromAPI(ctx context.Context, group string) (balance int64, userUID string, err error) {
+func (s *Sealos) fetchBalanceFromAPI(
+	ctx context.Context,
+	group string,
+) (balance int64, userUID string, err error) {
 	ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
 	defer cancel()
-	req, err := http.NewRequestWithContext(ctx, http.MethodGet,
-		fmt.Sprintf("%s/admin/v1alpha1/account-with-workspace?namespace=%s", s.accountURL, group), nil)
+	req, err := http.NewRequestWithContext(
+		ctx,
+		http.MethodGet,
+		fmt.Sprintf(
+			"%s/admin/v1alpha1/account-with-workspace?namespace=%s",
+			s.accountURL,
+			group,
+		),
+		nil,
+	)
 	if err != nil {
 		return 0, "", err
 	}
@@ -301,7 +323,11 @@ func (s *Sealos) fetchBalanceFromAPI(ctx context.Context, group string) (balance
 	}
 
 	if resp.StatusCode != http.StatusOK {
-		return 0, "", fmt.Errorf("get group (%s) balance failed with status code %d", group, resp.StatusCode)
+		return 0, "", fmt.Errorf(
+			"get group (%s) balance failed with status code %d",
+			group,
+			resp.StatusCode,
+		)
 	}
 
 	return sealosResp.Balance, sealosResp.UserUID, nil
@@ -321,7 +347,11 @@ func newSealosPostGroupConsumer(accountURL, group, uid string) *SealosPostGroupC
 	}
 }
 
-func (s *SealosPostGroupConsumer) PostGroupConsume(ctx context.Context, tokenName string, usage float64) (float64, error) {
+func (s *SealosPostGroupConsumer) PostGroupConsume(
+	ctx context.Context,
+	tokenName string,
+	usage float64,
+) (float64, error) {
 	amount := s.calculateAmount(usage)
 
 	if err := cacheDecreaseGroupBalance(ctx, s.group, amount.IntPart()); err != nil {
@@ -343,7 +373,11 @@ func (s *SealosPostGroupConsumer) calculateAmount(usage float64) decimal.Decimal
 	return amount
 }
 
-func (s *SealosPostGroupConsumer) postConsume(ctx context.Context, amount int64, tokenName string) error {
+func (s *SealosPostGroupConsumer) postConsume(
+	ctx context.Context,
+	amount int64,
+	tokenName string,
+) error {
 	reqBody, err := sonic.Marshal(sealosPostGroupConsumeReq{
 		Namespace: s.group,
 		Amount:    amount,

+ 1 - 1
core/common/env/helper.go

@@ -57,7 +57,7 @@ func Float64(env string, defaultValue float64) float64 {
 	return num
 }
 
-func String(env string, defaultValue string) string {
+func String(env, defaultValue string) string {
 	if env == "" {
 		return defaultValue
 	}

+ 12 - 2
core/common/fastJSONSerializer/fastJSONSerializer.go

@@ -12,7 +12,12 @@ import (
 
 type JSONSerializer struct{}
 
-func (*JSONSerializer) Scan(ctx context.Context, field *schema.Field, dst reflect.Value, dbValue any) (err error) {
+func (*JSONSerializer) Scan(
+	ctx context.Context,
+	field *schema.Field,
+	dst reflect.Value,
+	dbValue any,
+) (err error) {
 	fieldValue := reflect.New(field.FieldType)
 
 	if dbValue != nil {
@@ -38,7 +43,12 @@ func (*JSONSerializer) Scan(ctx context.Context, field *schema.Field, dst reflec
 	return
 }
 
-func (*JSONSerializer) Value(_ context.Context, _ *schema.Field, _ reflect.Value, fieldValue any) (any, error) {
+func (*JSONSerializer) Value(
+	_ context.Context,
+	_ *schema.Field,
+	_ reflect.Value,
+	fieldValue any,
+) (any, error) {
 	return sonic.Marshal(fieldValue)
 }
 

+ 3 - 4
core/common/image/image.go

@@ -7,7 +7,6 @@ import (
 	"errors"
 	"fmt"
 	"image"
-
 	// import gif decoder
 	_ "image/gif"
 	// import jpeg decoder
@@ -31,7 +30,7 @@ func IsImageURL(resp *http.Response) bool {
 	return strings.HasPrefix(resp.Header.Get("Content-Type"), "image/")
 }
 
-func GetImageSizeFromURL(url string) (width int, height int, err error) {
+func GetImageSizeFromURL(url string) (width, height int, err error) {
 	req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, url, nil)
 	if err != nil {
 		return 0, 0, err
@@ -109,7 +108,7 @@ func GetImageFromURL(ctx context.Context, url string) (string, string, error) {
 
 var reg = regexp.MustCompile(`data:image/([^;]+);base64,`)
 
-func GetImageSizeFromBase64(encoded string) (width int, height int, err error) {
+func GetImageSizeFromBase64(encoded string) (width, height int, err error) {
 	decoded, err := base64.StdEncoding.DecodeString(reg.ReplaceAllString(encoded, ""))
 	if err != nil {
 		return 0, 0, err
@@ -123,7 +122,7 @@ func GetImageSizeFromBase64(encoded string) (width int, height int, err error) {
 	return img.Width, img.Height, nil
 }
 
-func GetImageSize(image string) (width int, height int, err error) {
+func GetImageSize(image string) (width, height int, err error) {
 	if strings.HasPrefix(image, "data:image/") {
 		return GetImageSizeFromBase64(image)
 	}

+ 6 - 1
core/common/mcpproxy/sse.go

@@ -25,7 +25,12 @@ type SSEAProxy struct {
 }
 
 // NewSSEProxy creates a new proxy with the given backend and endpoint handler
-func NewSSEProxy(backend string, headers map[string]string, store SessionManager, endpoint EndpointProvider) *SSEAProxy {
+func NewSSEProxy(
+	backend string,
+	headers map[string]string,
+	store SessionManager,
+	endpoint EndpointProvider,
+) *SSEAProxy {
 	return &SSEAProxy{
 		store:    store,
 		endpoint: endpoint,

+ 20 - 18
core/common/mcpproxy/sse_test.go

@@ -20,7 +20,7 @@ func (t *TestSessionManager) New() string {
 }
 
 // Set stores a sessionID and its corresponding backend endpoint
-func (t *TestSessionManager) Set(sessionID string, endpoint string) {
+func (t *TestSessionManager) Set(sessionID, endpoint string) {
 	t.m[sessionID] = endpoint
 }
 
@@ -50,23 +50,25 @@ func (h *TestEndpointHandler) LoadEndpoint(endpoint string) string {
 func TestProxySSEEndpoint(t *testing.T) {
 	reqDone := make(chan struct{})
 	// Setup a mock backend server
-	backendServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
-		w.Header().Set("Content-Type", "text/event-stream")
-		w.Header().Set("Cache-Control", "no-cache")
-		w.Header().Set("Connection", "keep-alive")
-
-		flusher, ok := w.(http.Flusher)
-		if !ok {
-			t.Fatal("Expected ResponseWriter to be a Flusher")
-		}
-
-		// Send an endpoint event
-		fmt.Fprintf(w, "event: endpoint\n")
-		fmt.Fprintf(w, "data: /message?sessionId=original-session-id\n\n")
-		flusher.Flush()
-
-		close(reqDone)
-	}))
+	backendServer := httptest.NewServer(
+		http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
+			w.Header().Set("Content-Type", "text/event-stream")
+			w.Header().Set("Cache-Control", "no-cache")
+			w.Header().Set("Connection", "keep-alive")
+
+			flusher, ok := w.(http.Flusher)
+			if !ok {
+				t.Fatal("Expected ResponseWriter to be a Flusher")
+			}
+
+			// Send an endpoint event
+			fmt.Fprintf(w, "event: endpoint\n")
+			fmt.Fprintf(w, "data: /message?sessionId=original-session-id\n\n")
+			flusher.Flush()
+
+			close(reqDone)
+		}),
+	)
 	defer backendServer.Close()
 
 	// Create the proxy

+ 9 - 3
core/common/mcpproxy/streamable.go

@@ -18,7 +18,11 @@ type StreamableProxy struct {
 }
 
 // NewStreamableProxy creates a new proxy for the Streamable HTTP transport
-func NewStreamableProxy(backend string, headers map[string]string, store SessionManager) *StreamableProxy {
+func NewStreamableProxy(
+	backend string,
+	headers map[string]string,
+	store SessionManager,
+) *StreamableProxy {
 	return &StreamableProxy{
 		store:   store,
 		backend: backend,
@@ -113,7 +117,8 @@ func (p *StreamableProxy) handleGetRequest(w http.ResponseWriter, r *http.Reques
 	defer resp.Body.Close()
 
 	// Check if we got an SSE response
-	if resp.StatusCode != http.StatusOK || !strings.Contains(resp.Header.Get("Content-Type"), "text/event-stream") {
+	if resp.StatusCode != http.StatusOK ||
+		!strings.Contains(resp.Header.Get("Content-Type"), "text/event-stream") {
 		// Copy response headers, but not the backend session ID
 		for name, values := range resp.Header {
 			if name == "Mcp-Session-Id" {
@@ -386,7 +391,8 @@ func (p *StreamableProxy) proxyInitialOrNoSessionRequest(w http.ResponseWriter,
 		// Generate a new proxy session ID
 		proxySessionID := p.store.New()
 
-		// Store the mapping between our proxy session ID and the backend endpoint with its session ID
+		// Store the mapping between our proxy session ID and the backend endpoint with its session
+		// ID
 		backendURL := p.backend
 		if strings.Contains(backendURL, "?") {
 			backendURL += "&sessionId=" + backendSessionID

+ 1 - 1
core/common/network/ip.go

@@ -13,7 +13,7 @@ func IsValidSubnet(subnet string) error {
 	return nil
 }
 
-func IsIPInSubnet(ip string, subnet string) (bool, error) {
+func IsIPInSubnet(ip, subnet string) (bool, error) {
 	_, ipNet, err := net.ParseCIDR(subnet)
 	if err != nil {
 		return false, fmt.Errorf("failed to parse subnet: %w", err)

+ 6 - 1
core/common/notify/feishu.go

@@ -36,7 +36,12 @@ func (f *FeishuNotifier) Notify(level Level, title, message string) {
 	}()
 }
 
-func (f *FeishuNotifier) NotifyThrottle(level Level, key string, expiration time.Duration, title, message string) {
+func (f *FeishuNotifier) NotifyThrottle(
+	level Level,
+	key string,
+	expiration time.Duration,
+	title, message string,
+) {
 	if trylock.Lock(key, expiration) {
 		stdNotifier.Notify(level, title, message)
 		go func() {

+ 7 - 1
core/common/notify/notify.go

@@ -60,5 +60,11 @@ func WarnThrottle(key string, expiration time.Duration, title, message string) {
 }
 
 func ErrorThrottle(key string, expiration time.Duration, title, message string) {
-	defaultNotifier.NotifyThrottle(LevelError, limitKey(LevelError, key), expiration, title, message)
+	defaultNotifier.NotifyThrottle(
+		LevelError,
+		limitKey(LevelError, key),
+		expiration,
+		title,
+		message,
+	)
 }

+ 6 - 1
core/common/notify/std.go

@@ -40,7 +40,12 @@ func (n *StdNotifier) Notify(level Level, title, message string) {
 	}
 }
 
-func (n *StdNotifier) NotifyThrottle(level Level, key string, expiration time.Duration, title, message string) {
+func (n *StdNotifier) NotifyThrottle(
+	level Level,
+	key string,
+	expiration time.Duration,
+	title, message string,
+) {
 	if !trylock.MemLock(key, expiration) {
 		return
 	}

+ 1 - 1
core/common/redis.go

@@ -41,7 +41,7 @@ func InitRedisClient() (err error) {
 	return nil
 }
 
-func RedisSet(key string, value string, expiration time.Duration) error {
+func RedisSet(key, value string, expiration time.Duration) error {
 	ctx := context.Background()
 	return RDB.Set(ctx, key, value, expiration).Err()
 }

+ 2 - 1
core/common/render/event.go

@@ -28,7 +28,8 @@ func (r *OpenAISSE) Render(w http.ResponseWriter) error {
 		conv.StringToBytes(r.Data),
 		nnBytes,
 	} {
-		// nosemgrep: go.lang.security.audit.xss.no-direct-write-to-responsewriter.no-direct-write-to-responsewriter
+		// nosemgrep:
+		// go.lang.security.audit.xss.no-direct-write-to-responsewriter.no-direct-write-to-responsewriter
 		if _, err := w.Write(bytes); err != nil {
 			return err
 		}

+ 128 - 20
core/common/reqlimit/main.go

@@ -13,9 +13,20 @@ var (
 	redisGroupModelLimiter  = newRedisGroupModelRecord()
 )
 
-func PushGroupModelRequest(ctx context.Context, group, model string, overed int64) (int64, int64, int64) {
+func PushGroupModelRequest(
+	ctx context.Context,
+	group, model string,
+	overed int64,
+) (int64, int64, int64) {
 	if common.RedisEnabled {
-		count, overLimitCount, secondCount, err := redisGroupModelLimiter.PushRequest(ctx, overed, time.Minute, 1, group, model)
+		count, overLimitCount, secondCount, err := redisGroupModelLimiter.PushRequest(
+			ctx,
+			overed,
+			time.Minute,
+			1,
+			group,
+			model,
+		)
 		if err == nil {
 			return count, overLimitCount, secondCount
 		}
@@ -29,7 +40,12 @@ func GetGroupModelRequest(ctx context.Context, group, model string) (int64, int6
 		model = "*"
 	}
 	if common.RedisEnabled {
-		totalCount, secondCount, err := redisGroupModelLimiter.GetRequest(ctx, time.Minute, group, model)
+		totalCount, secondCount, err := redisGroupModelLimiter.GetRequest(
+			ctx,
+			time.Minute,
+			group,
+			model,
+		)
 		if err == nil {
 			return totalCount, secondCount
 		}
@@ -43,9 +59,20 @@ var (
 	redisGroupModelTokennameLimiter  = newRedisGroupModelTokennameRecord()
 )
 
-func PushGroupModelTokennameRequest(ctx context.Context, group, model, tokenname string) (int64, int64, int64) {
+func PushGroupModelTokennameRequest(
+	ctx context.Context,
+	group, model, tokenname string,
+) (int64, int64, int64) {
 	if common.RedisEnabled {
-		count, overLimitCount, secondCount, err := redisGroupModelTokennameLimiter.PushRequest(ctx, 0, time.Minute, 1, group, model, tokenname)
+		count, overLimitCount, secondCount, err := redisGroupModelTokennameLimiter.PushRequest(
+			ctx,
+			0,
+			time.Minute,
+			1,
+			group,
+			model,
+			tokenname,
+		)
 		if err == nil {
 			return count, overLimitCount, secondCount
 		}
@@ -54,7 +81,10 @@ func PushGroupModelTokennameRequest(ctx context.Context, group, model, tokenname
 	return memoryGroupModelTokennameLimiter.PushRequest(0, time.Minute, 1, group, model, tokenname)
 }
 
-func GetGroupModelTokennameRequest(ctx context.Context, group, model, tokenname string) (int64, int64) {
+func GetGroupModelTokennameRequest(
+	ctx context.Context,
+	group, model, tokenname string,
+) (int64, int64) {
 	if model == "" {
 		model = "*"
 	}
@@ -62,7 +92,13 @@ func GetGroupModelTokennameRequest(ctx context.Context, group, model, tokenname
 		tokenname = "*"
 	}
 	if common.RedisEnabled {
-		totalCount, secondCount, err := redisGroupModelTokennameLimiter.GetRequest(ctx, time.Minute, group, model, tokenname)
+		totalCount, secondCount, err := redisGroupModelTokennameLimiter.GetRequest(
+			ctx,
+			time.Minute,
+			group,
+			model,
+			tokenname,
+		)
 		if err == nil {
 			return totalCount, secondCount
 		}
@@ -78,7 +114,14 @@ var (
 
 func PushChannelModelRequest(ctx context.Context, channel, model string) (int64, int64, int64) {
 	if common.RedisEnabled {
-		count, overLimitCount, secondCount, err := redisChannelModelRecord.PushRequest(ctx, 0, time.Minute, 1, channel, model)
+		count, overLimitCount, secondCount, err := redisChannelModelRecord.PushRequest(
+			ctx,
+			0,
+			time.Minute,
+			1,
+			channel,
+			model,
+		)
 		if err == nil {
 			return count, overLimitCount, secondCount
 		}
@@ -95,7 +138,12 @@ func GetChannelModelRequest(ctx context.Context, channel, model string) (int64,
 		model = "*"
 	}
 	if common.RedisEnabled {
-		totalCount, secondCount, err := redisChannelModelRecord.GetRequest(ctx, time.Minute, channel, model)
+		totalCount, secondCount, err := redisChannelModelRecord.GetRequest(
+			ctx,
+			time.Minute,
+			channel,
+			model,
+		)
 		if err == nil {
 			return totalCount, secondCount
 		}
@@ -109,9 +157,20 @@ var (
 	redisGroupModelTokensLimiter  = newRedisGroupModelTokensRecord()
 )
 
-func PushGroupModelTokensRequest(ctx context.Context, group, model string, maxTokens int64, tokens int64) (int64, int64, int64) {
+func PushGroupModelTokensRequest(
+	ctx context.Context,
+	group, model string,
+	maxTokens, tokens int64,
+) (int64, int64, int64) {
 	if common.RedisEnabled {
-		count, overLimitCount, secondCount, err := redisGroupModelTokensLimiter.PushRequest(ctx, maxTokens, time.Minute, tokens, group, model)
+		count, overLimitCount, secondCount, err := redisGroupModelTokensLimiter.PushRequest(
+			ctx,
+			maxTokens,
+			time.Minute,
+			tokens,
+			group,
+			model,
+		)
 		if err == nil {
 			return count, overLimitCount, secondCount
 		}
@@ -125,7 +184,12 @@ func GetGroupModelTokensRequest(ctx context.Context, group, model string) (int64
 		model = "*"
 	}
 	if common.RedisEnabled {
-		totalCount, secondCount, err := redisGroupModelTokensLimiter.GetRequest(ctx, time.Minute, group, model)
+		totalCount, secondCount, err := redisGroupModelTokensLimiter.GetRequest(
+			ctx,
+			time.Minute,
+			group,
+			model,
+		)
 		if err == nil {
 			return totalCount, secondCount
 		}
@@ -139,18 +203,40 @@ var (
 	redisGroupModelTokennameTokensLimiter  = newRedisGroupModelTokennameTokensRecord()
 )
 
-func PushGroupModelTokennameTokensRequest(ctx context.Context, group, model, tokenname string, tokens int64) (int64, int64, int64) {
+func PushGroupModelTokennameTokensRequest(
+	ctx context.Context,
+	group, model, tokenname string,
+	tokens int64,
+) (int64, int64, int64) {
 	if common.RedisEnabled {
-		count, overLimitCount, secondCount, err := redisGroupModelTokennameTokensLimiter.PushRequest(ctx, 0, time.Minute, tokens, group, model, tokenname)
+		count, overLimitCount, secondCount, err := redisGroupModelTokennameTokensLimiter.PushRequest(
+			ctx,
+			0,
+			time.Minute,
+			tokens,
+			group,
+			model,
+			tokenname,
+		)
 		if err == nil {
 			return count, overLimitCount, secondCount
 		}
 		log.Error("redis push request error: " + err.Error())
 	}
-	return memoryGroupModelTokennameTokensLimiter.PushRequest(0, time.Minute, tokens, group, model, tokenname)
+	return memoryGroupModelTokennameTokensLimiter.PushRequest(
+		0,
+		time.Minute,
+		tokens,
+		group,
+		model,
+		tokenname,
+	)
 }
 
-func GetGroupModelTokennameTokensRequest(ctx context.Context, group, model, tokenname string) (int64, int64) {
+func GetGroupModelTokennameTokensRequest(
+	ctx context.Context,
+	group, model, tokenname string,
+) (int64, int64) {
 	if model == "" {
 		model = "*"
 	}
@@ -158,7 +244,13 @@ func GetGroupModelTokennameTokensRequest(ctx context.Context, group, model, toke
 		tokenname = "*"
 	}
 	if common.RedisEnabled {
-		totalCount, secondCount, err := redisGroupModelTokennameTokensLimiter.GetRequest(ctx, time.Minute, group, model, tokenname)
+		totalCount, secondCount, err := redisGroupModelTokennameTokensLimiter.GetRequest(
+			ctx,
+			time.Minute,
+			group,
+			model,
+			tokenname,
+		)
 		if err == nil {
 			return totalCount, secondCount
 		}
@@ -172,9 +264,20 @@ var (
 	redisChannelModelTokensRecord  = newRedisChannelModelTokensRecord()
 )
 
-func PushChannelModelTokensRequest(ctx context.Context, channel, model string, tokens int64) (int64, int64, int64) {
+func PushChannelModelTokensRequest(
+	ctx context.Context,
+	channel, model string,
+	tokens int64,
+) (int64, int64, int64) {
 	if common.RedisEnabled {
-		count, overLimitCount, secondCount, err := redisChannelModelTokensRecord.PushRequest(ctx, 0, time.Minute, tokens, channel, model)
+		count, overLimitCount, secondCount, err := redisChannelModelTokensRecord.PushRequest(
+			ctx,
+			0,
+			time.Minute,
+			tokens,
+			channel,
+			model,
+		)
 		if err == nil {
 			return count, overLimitCount, secondCount
 		}
@@ -191,7 +294,12 @@ func GetChannelModelTokensRequest(ctx context.Context, channel, model string) (i
 		model = "*"
 	}
 	if common.RedisEnabled {
-		totalCount, secondCount, err := redisChannelModelTokensRecord.GetRequest(ctx, time.Minute, channel, model)
+		totalCount, secondCount, err := redisChannelModelTokensRecord.GetRequest(
+			ctx,
+			time.Minute,
+			channel,
+			model,
+		)
 		if err == nil {
 			return totalCount, secondCount
 		}

+ 12 - 4
core/common/reqlimit/mem.go

@@ -56,7 +56,12 @@ func (m *InMemoryRecord) cleanupAndCount(e *entry, cutoff int64) (int64, int64)
 	return normalCount, overCount
 }
 
-func (m *InMemoryRecord) PushRequest(overed int64, duration time.Duration, n int64, keys ...string) (normalCount int64, overCount int64, secondCount int64) {
+func (m *InMemoryRecord) PushRequest(
+	overed int64,
+	duration time.Duration,
+	n int64,
+	keys ...string,
+) (normalCount, overCount, secondCount int64) {
 	e := m.getEntry(keys)
 
 	e.Lock()
@@ -87,7 +92,10 @@ func (m *InMemoryRecord) PushRequest(overed int64, duration time.Duration, n int
 	return normalCount, overCount, wc.normal + wc.over
 }
 
-func (m *InMemoryRecord) GetRequest(duration time.Duration, keys ...string) (totalCount int64, secondCount int64) {
+func (m *InMemoryRecord) GetRequest(
+	duration time.Duration,
+	keys ...string,
+) (totalCount, secondCount int64) {
 	nowSecond := time.Now().Unix()
 	cutoff := nowSecond - int64(duration.Seconds())
 
@@ -112,7 +120,7 @@ func (m *InMemoryRecord) GetRequest(duration time.Duration, keys ...string) (tot
 	return totalCount, secondCount
 }
 
-func (m *InMemoryRecord) cleanupInactiveEntries(interval time.Duration, maxInactivity time.Duration) {
+func (m *InMemoryRecord) cleanupInactiveEntries(interval, maxInactivity time.Duration) {
 	ticker := time.NewTicker(interval)
 	defer ticker.Stop()
 	for range ticker.C {
@@ -135,7 +143,7 @@ func parseKeys(key string) []string {
 	return strings.Split(key, ":")
 }
 
-func matchKeys(pattern []string, keys []string) bool {
+func matchKeys(pattern, keys []string) bool {
 	if len(pattern) != len(keys) {
 		return false
 	}

+ 5 - 1
core/common/reqlimit/mem_test.go

@@ -231,7 +231,11 @@ func TestEmptyQueries(t *testing.T) {
 
 	totalCount, secondCount = rl.GetRequest(60*time.Second, "nonexistent", "model")
 	if totalCount != 0 || secondCount != 0 {
-		t.Errorf("Expected empty results for nonexistent key, got total=%d, second=%d", totalCount, secondCount)
+		t.Errorf(
+			"Expected empty results for nonexistent key, got total=%d, second=%d",
+			totalCount,
+			secondCount,
+		)
 	}
 }
 

+ 12 - 2
core/common/reqlimit/redis.go

@@ -148,7 +148,11 @@ func (r *redisRateRecord) buildKey(keys ...string) string {
 	return r.prefix + ":" + strings.Join(keys, ":")
 }
 
-func (r *redisRateRecord) GetRequest(ctx context.Context, duration time.Duration, keys ...string) (totalCount int64, secondCount int64, err error) {
+func (r *redisRateRecord) GetRequest(
+	ctx context.Context,
+	duration time.Duration,
+	keys ...string,
+) (totalCount, secondCount int64, err error) {
 	if !common.RedisEnabled {
 		return 0, 0, nil
 	}
@@ -184,7 +188,13 @@ func (r *redisRateRecord) GetRequest(ctx context.Context, duration time.Duration
 	return totalCountInt, secondCountInt, nil
 }
 
-func (r *redisRateRecord) PushRequest(ctx context.Context, overed int64, duration time.Duration, n int64, keys ...string) (normalCount int64, overCount int64, secondCount int64, err error) {
+func (r *redisRateRecord) PushRequest(
+	ctx context.Context,
+	overed int64,
+	duration time.Duration,
+	n int64,
+	keys ...string,
+) (normalCount, overCount, secondCount int64, err error) {
 	key := r.buildKey(keys...)
 
 	result, err := pushRequestScript.Run(

+ 9 - 6
core/common/splitter/splitter.go

@@ -30,12 +30,15 @@ func NewSplitter(heads, tails [][]byte) *Splitter {
 	}
 
 	return &Splitter{
-		heads:           heads,
-		tails:           tails,
-		kmpNexts:        kmpNexts,
-		partialTailPos:  make([]int, len(tails)),
-		impossibleHeads: make([]bool, len(heads)), // Defaults to false (all heads initially possible)
-		longestHeadLen:  longestHeadLen,
+		heads:          heads,
+		tails:          tails,
+		kmpNexts:       kmpNexts,
+		partialTailPos: make([]int, len(tails)),
+		impossibleHeads: make(
+			[]bool,
+			len(heads),
+		), // Defaults to false (all heads initially possible)
+		longestHeadLen: longestHeadLen,
 	}
 }
 

+ 45 - 6
core/controller/channel-billing.go

@@ -19,19 +19,42 @@ import (
 func updateChannelBalance(channel *model.Channel) (float64, error) {
 	adaptorI, ok := adaptors.GetAdaptor(channel.Type)
 	if !ok {
-		return 0, fmt.Errorf("invalid channel type: %d, channel: %s (id: %d)", channel.Type, channel.Name, channel.ID)
+		return 0, fmt.Errorf(
+			"invalid channel type: %d, channel: %s (id: %d)",
+			channel.Type,
+			channel.Name,
+			channel.ID,
+		)
 	}
 	if getBalance, ok := adaptorI.(adaptor.Balancer); ok {
 		balance, err := getBalance.GetBalance(channel)
 		if err != nil && !errors.Is(err, adaptor.ErrGetBalanceNotImplemented) {
-			return 0, fmt.Errorf("failed to get channel %s (type: %d, id: %d) balance: %s", channel.Name, channel.Type, channel.ID, err.Error())
+			return 0, fmt.Errorf(
+				"failed to get channel %s (type: %d, id: %d) balance: %s",
+				channel.Name,
+				channel.Type,
+				channel.ID,
+				err.Error(),
+			)
 		}
 		if err := channel.UpdateBalance(balance); err != nil {
-			return 0, fmt.Errorf("failed to update channel %s (type: %d, id: %d) balance: %s", channel.Name, channel.Type, channel.ID, err.Error())
+			return 0, fmt.Errorf(
+				"failed to update channel %s (type: %d, id: %d) balance: %s",
+				channel.Name,
+				channel.Type,
+				channel.ID,
+				err.Error(),
+			)
 		}
 		if !errors.Is(err, adaptor.ErrGetBalanceNotImplemented) &&
 			balance < channel.GetBalanceThreshold() {
-			return 0, fmt.Errorf("channel %s (type: %d, id: %d) balance is less than threshold: %f", channel.Name, channel.Type, channel.ID, balance)
+			return 0, fmt.Errorf(
+				"channel %s (type: %d, id: %d) balance is less than threshold: %f",
+				channel.Name,
+				channel.Type,
+				channel.ID,
+				balance,
+			)
 		}
 		return balance, nil
 	}
@@ -67,7 +90,15 @@ func UpdateChannelBalance(c *gin.Context) {
 	}
 	balance, err := updateChannelBalance(channel)
 	if err != nil {
-		notify.Error(fmt.Sprintf("check channel %s (type: %d, id: %d) balance error", channel.Name, channel.Type, channel.ID), err.Error())
+		notify.Error(
+			fmt.Sprintf(
+				"check channel %s (type: %d, id: %d) balance error",
+				channel.Name,
+				channel.Type,
+				channel.ID,
+			),
+			err.Error(),
+		)
 		c.JSON(http.StatusOK, middleware.APIResponse{
 			Success: false,
 			Message: err.Error(),
@@ -97,7 +128,15 @@ func updateAllChannelsBalance() error {
 			defer func() { <-semaphore }()
 			_, err := updateChannelBalance(ch)
 			if err != nil {
-				notify.Error(fmt.Sprintf("check channel %s (type: %d, id: %d) balance error", ch.Name, ch.Type, ch.ID), err.Error())
+				notify.Error(
+					fmt.Sprintf(
+						"check channel %s (type: %d, id: %d) balance error",
+						ch.Name,
+						ch.Type,
+						ch.ID,
+					),
+					err.Error(),
+				)
 			}
 		}(channel)
 	}

+ 62 - 9
core/controller/channel-test.go

@@ -55,7 +55,11 @@ func guessModelConfig(modelName string) model.ModelConfig {
 }
 
 // testSingleModel tests a single model in the channel
-func testSingleModel(mc *model.ModelCaches, channel *model.Channel, modelName string) (*model.ChannelTest, error) {
+func testSingleModel(
+	mc *model.ModelCaches,
+	channel *model.Channel,
+	modelName string,
+) (*model.ChannelTest, error) {
 	modelConfig, ok := mc.ModelConfig.GetModelConfig(modelName)
 	if !ok {
 		return nil, errors.New(modelName + " model config not found")
@@ -184,10 +188,22 @@ func TestChannel(c *gin.Context) {
 
 	ct, err := testSingleModel(model.LoadModelCaches(), channel, modelName)
 	if err != nil {
-		log.Errorf("failed to test channel %s(%d) model %s: %s", channel.Name, channel.ID, modelName, err.Error())
+		log.Errorf(
+			"failed to test channel %s(%d) model %s: %s",
+			channel.Name,
+			channel.ID,
+			modelName,
+			err.Error(),
+		)
 		c.JSON(http.StatusOK, middleware.APIResponse{
 			Success: false,
-			Message: fmt.Sprintf("failed to test channel %s(%d) model %s: %s", channel.Name, channel.ID, modelName, err.Error()),
+			Message: fmt.Sprintf(
+				"failed to test channel %s(%d) model %s: %s",
+				channel.Name,
+				channel.ID,
+				modelName,
+				err.Error(),
+			),
 		})
 		return
 	}
@@ -208,7 +224,12 @@ type TestResult struct {
 	Success bool               `json:"success"`
 }
 
-func processTestResult(mc *model.ModelCaches, channel *model.Channel, modelName string, returnSuccess bool, successResponseBody bool) *TestResult {
+func processTestResult(
+	mc *model.ModelCaches,
+	channel *model.Channel,
+	modelName string,
+	returnSuccess, successResponseBody bool,
+) *TestResult {
 	ct, err := testSingleModel(mc, channel, modelName)
 
 	e := &utils.UnsupportedModelTypeError{}
@@ -221,7 +242,13 @@ func processTestResult(mc *model.ModelCaches, channel *model.Channel, modelName
 		Success: err == nil,
 	}
 	if err != nil {
-		result.Message = fmt.Sprintf("failed to test channel %s(%d) model %s: %s", channel.Name, channel.ID, modelName, err.Error())
+		result.Message = fmt.Sprintf(
+			"failed to test channel %s(%d) model %s: %s",
+			channel.Name,
+			channel.ID,
+			modelName,
+			err.Error(),
+		)
 		return result
 	}
 
@@ -324,7 +351,12 @@ func TestChannelModels(c *gin.Context) {
 	if !hasError.Load() {
 		err := model.ClearLastTestErrorAt(channel.ID)
 		if err != nil {
-			log.Errorf("failed to clear last test error at for channel %s(%d): %s", channel.Name, channel.ID, err.Error())
+			log.Errorf(
+				"failed to clear last test error at for channel %s(%d): %s",
+				channel.Name,
+				channel.ID,
+				err.Error(),
+			)
 		}
 	}
 
@@ -442,7 +474,10 @@ func TestAllChannels(c *gin.Context) {
 }
 
 func tryTestChannel(channelID int, modelName string) bool {
-	return trylock.Lock(fmt.Sprintf("channel_test_lock:%d:%s", channelID, modelName), 30*time.Second)
+	return trylock.Lock(
+		fmt.Sprintf("channel_test_lock:%d:%s", channelID, modelName),
+		30*time.Second,
+	)
 }
 
 func AutoTestBannedModels() {
@@ -472,11 +507,29 @@ func AutoTestBannedModels() {
 			}
 			result, err := testSingleModel(mc, channel, modelName)
 			if err != nil {
-				notify.Error(fmt.Sprintf("channel %s (type: %d, id: %d) model %s test failed", channel.Name, channel.Type, channel.ID, modelName), err.Error())
+				notify.Error(
+					fmt.Sprintf(
+						"channel %s (type: %d, id: %d) model %s test failed",
+						channel.Name,
+						channel.Type,
+						channel.ID,
+						modelName,
+					),
+					err.Error(),
+				)
 				continue
 			}
 			if result.Success {
-				notify.Info(fmt.Sprintf("channel %s (type: %d, id: %d) model %s test success", channel.Name, channel.Type, channel.ID, modelName), "unban it")
+				notify.Info(
+					fmt.Sprintf(
+						"channel %s (type: %d, id: %d) model %s test success",
+						channel.Name,
+						channel.Type,
+						channel.ID,
+						modelName,
+					),
+					"unban it",
+				)
 				err = monitor.ClearChannelModelErrors(context.Background(), modelName, channel.ID)
 				if err != nil {
 					log.Errorf("clear channel errors failed: %+v", err)

+ 36 - 4
core/controller/channel.go

@@ -57,7 +57,16 @@ func GetChannels(c *gin.Context) {
 	channelType, _ := strconv.Atoi(c.Query("channel_type"))
 	baseURL := c.Query("base_url")
 	order := c.Query("order")
-	channels, total, err := model.GetChannels(page, perPage, id, name, key, channelType, baseURL, order)
+	channels, total, err := model.GetChannels(
+		page,
+		perPage,
+		id,
+		name,
+		key,
+		channelType,
+		baseURL,
+		order,
+	)
 	if err != nil {
 		middleware.ErrorResponse(c, http.StatusInternalServerError, err.Error())
 		return
@@ -148,7 +157,17 @@ func SearchChannels(c *gin.Context) {
 	channelType, _ := strconv.Atoi(c.Query("channel_type"))
 	baseURL := c.Query("base_url")
 	order := c.Query("order")
-	channels, total, err := model.SearchChannels(keyword, page, perPage, id, name, key, channelType, baseURL, order)
+	channels, total, err := model.SearchChannels(
+		keyword,
+		page,
+		perPage,
+		id,
+		name,
+		key,
+		channelType,
+		baseURL,
+		order,
+	)
 	if err != nil {
 		middleware.ErrorResponse(c, http.StatusInternalServerError, err.Error())
 		return
@@ -207,9 +226,22 @@ func (r *AddChannelRequest) ToChannel() (*model.Channel, error) {
 		if err != nil {
 			keyHelp := validator.KeyHelp()
 			if keyHelp == "" {
-				return nil, fmt.Errorf("%s [%s(%d)] invalid key: %w", r.Name, r.Type.String(), r.Type, err)
+				return nil, fmt.Errorf(
+					"%s [%s(%d)] invalid key: %w",
+					r.Name,
+					r.Type.String(),
+					r.Type,
+					err,
+				)
 			}
-			return nil, fmt.Errorf("%s [%s(%d)] invalid key: %w, %s", r.Name, r.Type.String(), r.Type, err, keyHelp)
+			return nil, fmt.Errorf(
+				"%s [%s(%d)] invalid key: %w, %s",
+				r.Name,
+				r.Type.String(),
+				r.Type,
+				err,
+				keyHelp,
+			)
 		}
 	}
 	if r.Config != nil {

+ 61 - 10
core/controller/dashboard.go

@@ -15,7 +15,11 @@ import (
 	"gorm.io/gorm"
 )
 
-func getDashboardTime(t string, timespan string, startTimestamp int64, endTimestamp int64, timezoneLocation *time.Location) (time.Time, time.Time, model.TimeSpanType) {
+func getDashboardTime(
+	t, timespan string,
+	startTimestamp, endTimestamp int64,
+	timezoneLocation *time.Location,
+) (time.Time, time.Time, model.TimeSpanType) {
 	end := time.Now()
 	if endTimestamp != 0 {
 		end = time.Unix(endTimestamp, 0)
@@ -54,7 +58,11 @@ func getDashboardTime(t string, timespan string, startTimestamp int64, endTimest
 	return start, end, timeSpan
 }
 
-func fillGaps(data []*model.ChartData, start, end time.Time, t model.TimeSpanType) []*model.ChartData {
+func fillGaps(
+	data []*model.ChartData,
+	start, end time.Time,
+	t model.TimeSpanType,
+) []*model.ChartData {
 	if len(data) == 0 {
 		return data
 	}
@@ -162,12 +170,25 @@ func GetDashboard(c *gin.Context) {
 	endTimestamp, _ := strconv.ParseInt(c.Query("end_timestamp"), 10, 64)
 	timezoneLocation, _ := time.LoadLocation(c.DefaultQuery("timezone", "Local"))
 	timespan := c.Query("timespan")
-	start, end, timeSpan := getDashboardTime(c.Query("type"), timespan, startTimestamp, endTimestamp, timezoneLocation)
+	start, end, timeSpan := getDashboardTime(
+		c.Query("type"),
+		timespan,
+		startTimestamp,
+		endTimestamp,
+		timezoneLocation,
+	)
 	modelName := c.Query("model")
 	channelStr := c.Query("channel")
 	channelID, _ := strconv.Atoi(channelStr)
 
-	dashboards, err := model.GetDashboardData(start, end, modelName, channelID, timeSpan, timezoneLocation)
+	dashboards, err := model.GetDashboardData(
+		start,
+		end,
+		modelName,
+		channelID,
+		timeSpan,
+		timezoneLocation,
+	)
 	if err != nil {
 		middleware.ErrorResponse(c, http.StatusInternalServerError, err.Error())
 		return
@@ -214,11 +235,25 @@ func GetGroupDashboard(c *gin.Context) {
 	endTimestamp, _ := strconv.ParseInt(c.Query("end_timestamp"), 10, 64)
 	timezoneLocation, _ := time.LoadLocation(c.DefaultQuery("timezone", "Local"))
 	timespan := c.Query("timespan")
-	start, end, timeSpan := getDashboardTime(c.Query("type"), timespan, startTimestamp, endTimestamp, timezoneLocation)
+	start, end, timeSpan := getDashboardTime(
+		c.Query("type"),
+		timespan,
+		startTimestamp,
+		endTimestamp,
+		timezoneLocation,
+	)
 	tokenName := c.Query("token_name")
 	modelName := c.Query("model")
 
-	dashboards, err := model.GetGroupDashboardData(group, start, end, tokenName, modelName, timeSpan, timezoneLocation)
+	dashboards, err := model.GetGroupDashboardData(
+		group,
+		start,
+		end,
+		tokenName,
+		modelName,
+		timeSpan,
+		timezoneLocation,
+	)
 	if err != nil {
 		middleware.ErrorResponse(c, http.StatusInternalServerError, "failed to get statistics")
 		return
@@ -226,9 +261,19 @@ func GetGroupDashboard(c *gin.Context) {
 
 	dashboards.ChartData = fillGaps(dashboards.ChartData, start, end, timeSpan)
 
-	rpm, _ := reqlimit.GetGroupModelTokennameRequest(c.Request.Context(), group, modelName, tokenName)
+	rpm, _ := reqlimit.GetGroupModelTokennameRequest(
+		c.Request.Context(),
+		group,
+		modelName,
+		tokenName,
+	)
 	dashboards.RPM = rpm
-	tpm, _ := reqlimit.GetGroupModelTokennameTokensRequest(c.Request.Context(), group, modelName, tokenName)
+	tpm, _ := reqlimit.GetGroupModelTokennameTokensRequest(
+		c.Request.Context(),
+		group,
+		modelName,
+		tokenName,
+	)
 	dashboards.TPM = tpm
 
 	middleware.SuccessResponse(c, dashboards)
@@ -253,7 +298,10 @@ func GetGroupDashboardModels(c *gin.Context) {
 	groupCache, err := model.CacheGetGroup(group)
 	if err != nil {
 		if errors.Is(err, gorm.ErrRecordNotFound) {
-			middleware.SuccessResponse(c, model.LoadModelCaches().EnabledModelConfigsBySet[model.ChannelDefaultSet])
+			middleware.SuccessResponse(
+				c,
+				model.LoadModelCaches().EnabledModelConfigsBySet[model.ChannelDefaultSet],
+			)
 		} else {
 			middleware.ErrorResponse(c, http.StatusInternalServerError, fmt.Sprintf("failed to get group: %v", err))
 		}
@@ -270,7 +318,10 @@ func GetGroupDashboardModels(c *gin.Context) {
 			}) {
 				continue
 			}
-			newEnabledModelConfigs = append(newEnabledModelConfigs, middleware.GetGroupAdjustedModelConfig(groupCache, mc))
+			newEnabledModelConfigs = append(
+				newEnabledModelConfigs,
+				middleware.GetGroupAdjustedModelConfig(groupCache, mc),
+			)
 		}
 	}
 	middleware.SuccessResponse(c, newEnabledModelConfigs)

+ 23 - 10
core/controller/embedmcp.go

@@ -15,11 +15,10 @@ import (
 	"github.com/labring/aiproxy/core/middleware"
 	"github.com/labring/aiproxy/core/model"
 	mcpservers "github.com/labring/aiproxy/mcp-servers"
-	"github.com/mark3labs/mcp-go/mcp"
-	"github.com/mark3labs/mcp-go/server"
-
 	// init embed mcp
 	_ "github.com/labring/aiproxy/mcp-servers/mcpregister"
+	"github.com/mark3labs/mcp-go/mcp"
+	"github.com/mark3labs/mcp-go/server"
 )
 
 type EmbedMCPConfigTemplate struct {
@@ -101,7 +100,10 @@ type SaveEmbedMCPRequest struct {
 	InitConfig map[string]string `json:"init_config"`
 }
 
-func GetEmbedConfig(ct mcpservers.ConfigTemplates, initConfig map[string]string) (*model.MCPEmbeddingConfig, error) {
+func GetEmbedConfig(
+	ct mcpservers.ConfigTemplates,
+	initConfig map[string]string,
+) (*model.MCPEmbeddingConfig, error) {
 	reusingConfig := make(map[string]model.MCPEmbeddingReusingConfig)
 	embedConfig := &model.MCPEmbeddingConfig{
 		Init: initConfig,
@@ -139,7 +141,11 @@ func GetEmbedConfig(ct mcpservers.ConfigTemplates, initConfig map[string]string)
 	return embedConfig, nil
 }
 
-func ToPublicMCP(e mcpservers.EmbedMcp, initConfig map[string]string, enabled bool) (*model.PublicMCP, error) {
+func ToPublicMCP(
+	e mcpservers.EmbedMcp,
+	initConfig map[string]string,
+	enabled bool,
+) (*model.PublicMCP, error) {
 	embedConfig, err := GetEmbedConfig(e.ConfigTemplates, initConfig)
 	if err != nil {
 		return nil, err
@@ -221,7 +227,8 @@ func (m *testEmbedMcpEndpointProvider) LoadEndpoint(endpoint string) (session st
 	return parsedURL.Query().Get("sessionId")
 }
 
-// query like: /api/test-embedmcp/aiproxy-openapi/sse?key=adminkey&config[key1]=value1&config[key2]=value2&reusing[key3]=value3
+// query like:
+// /api/test-embedmcp/aiproxy-openapi/sse?key=adminkey&config[key1]=value1&config[key2]=value2&reusing[key3]=value3
 func getConfigFromQuery(c *gin.Context) (map[string]string, map[string]string) {
 	initConfig := make(map[string]string)
 	reusingConfig := make(map[string]string)
@@ -260,8 +267,11 @@ func getConfigFromQuery(c *gin.Context) (map[string]string, map[string]string) {
 //	@Tags			embedmcp
 //	@Security		ApiKeyAuth
 //	@Param			id				path		string	true	"MCP ID"
-//	@Param			config[key]		query		string	false	"Initial configuration parameters (e.g., config[host]=http://localhost:3000)"
-//	@Param			reusing[key]	query		string	false	"Reusing configuration parameters (e.g., reusing[authorization]=apikey)"
+//	@Param			config[key]		query		string	false	"Initial configuration parameters (e.g.,
+//
+// config[host]=http://localhost:3000)" 	@Param			reusing[key]	query		string	false	"Reusing
+// configuration parameters (e.g., reusing[authorization]=apikey)"
+//
 //	@Success		200				{object}	nil
 //	@Failure		400				{object}	nil
 //	@Router			/api/test-embedmcp/{id}/sse [get]
@@ -351,8 +361,11 @@ func TestEmbedMCPMessage(c *gin.Context) {
 //	@Tags			embedmcp
 //	@Security		ApiKeyAuth
 //	@Param			id				path	string	true	"MCP ID"
-//	@Param			config[key]		query	string	false	"Initial configuration parameters (e.g., config[host]=http://localhost:3000)"
-//	@Param			reusing[key]	query	string	false	"Reusing configuration parameters (e.g., reusing[authorization]=apikey)"
+//	@Param			config[key]		query	string	false	"Initial configuration parameters (e.g.,
+//
+// config[host]=http://localhost:3000)" 	@Param			reusing[key]	query	string	false	"Reusing
+// configuration parameters (e.g., reusing[authorization]=apikey)"
+//
 //	@Accept			json
 //	@Produce		json
 //	@Success		200	{object}	nil

+ 8 - 1
core/controller/groupmcp.go

@@ -36,7 +36,14 @@ func GetGroupMCPs(c *gin.Context) {
 	keyword := c.Query("keyword")
 	status, _ := strconv.Atoi(c.Query("status"))
 
-	mcps, total, err := model.GetGroupMCPs(groupID, page, perPage, mcpType, keyword, model.GroupMCPStatus(status))
+	mcps, total, err := model.GetGroupMCPs(
+		groupID,
+		page,
+		perPage,
+		mcpType,
+		keyword,
+		model.GroupMCPStatus(status),
+	)
 	if err != nil {
 		middleware.ErrorResponse(c, http.StatusInternalServerError, err.Error())
 		return

+ 6 - 2
core/controller/import.go

@@ -16,7 +16,7 @@ type OneAPIChannel struct {
 	Status       int               `gorm:"default:1"                              json:"status"`
 	Name         string            `gorm:"index"                                  json:"name"`
 	BaseURL      string            `gorm:"column:base_url;default:''"`
-	Models       string            `json:"models"`
+	Models       string            `                                              json:"models"`
 	ModelMapping map[string]string `gorm:"type:varchar(1024);serializer:fastjson"`
 	Priority     int32             `gorm:"bigint;default:0"`
 	Config       ChannelConfig     `gorm:"serializer:fastjson"`
@@ -188,7 +188,11 @@ func ImportChannelFromOneAPI(c *gin.Context) {
 	case strings.HasPrefix(req.DSN, "postgres"):
 		db, err = model.OpenPostgreSQL(req.DSN)
 	default:
-		middleware.ErrorResponse(c, http.StatusBadRequest, "invalid dsn, only mysql and postgres are supported")
+		middleware.ErrorResponse(
+			c,
+			http.StatusBadRequest,
+			"invalid dsn, only mysql and postgres are supported",
+		)
 		return
 	}
 

+ 7 - 1
core/controller/modelconfig.go

@@ -98,7 +98,13 @@ func SearchModelConfigs(c *gin.Context) {
 	page, perPage := parsePageParams(c)
 	_model := c.Query("model")
 	owner := c.Query("owner")
-	configs, total, err := model.SearchModelConfigs(keyword, page, perPage, _model, model.ModelOwner(owner))
+	configs, total, err := model.SearchModelConfigs(
+		keyword,
+		page,
+		perPage,
+		_model,
+		model.ModelOwner(owner),
+	)
 	if err != nil {
 		middleware.ErrorResponse(c, http.StatusInternalServerError, err.Error())
 		return

+ 17 - 5
core/controller/publicmcp-server.go

@@ -97,12 +97,14 @@ func (r *redisStoreManager) New() string {
 func (r *redisStoreManager) Get(sessionID string) (string, bool) {
 	ctx := context.Background()
 
-	result, err := redisStoreManagerScript.Run(ctx, r.rdb, []string{"mcp:session:" + sessionID}).Result()
+	result, err := redisStoreManagerScript.Run(ctx, r.rdb, []string{"mcp:session:" + sessionID}).
+		Result()
 	if err != nil || result == nil {
 		return "", false
 	}
 
-	return result.(string), true
+	res, ok := result.(string)
+	return res, ok
 }
 
 func (r *redisStoreManager) Set(sessionID, endpoint string) {
@@ -333,7 +335,11 @@ func parseOpenAPIFromContent(config *model.MCPOpenAPIConfig, parser *convert.Par
 }
 
 // processMCPSseMpscMessages handles message processing for OpenAPI
-func processMCPSseMpscMessages(ctx context.Context, sessionID string, server *statelessmcp.SSEServer) {
+func processMCPSseMpscMessages(
+	ctx context.Context,
+	sessionID string,
+	server *statelessmcp.SSEServer,
+) {
 	mpscInstance := getMCPMpsc()
 	for {
 		select {
@@ -352,7 +358,12 @@ func processMCPSseMpscMessages(ctx context.Context, sessionID string, server *st
 }
 
 // processReusingParams handles the reusing parameters for MCP proxy
-func processReusingParams(reusingParams map[string]model.ReusingParam, mcpID string, groupID string, headers map[string]string, backendQuery *url.Values) error {
+func processReusingParams(
+	reusingParams map[string]model.ReusingParam,
+	mcpID, groupID string,
+	headers map[string]string,
+	backendQuery *url.Values,
+) error {
 	if len(reusingParams) == 0 {
 		return nil
 	}
@@ -668,7 +679,8 @@ func newChannelMCPMpsc() *channelMCPMpsc {
 	return c
 }
 
-// cleanupExpiredChannels periodically checks for and removes channels that haven't been accessed in 5 minutes
+// cleanupExpiredChannels periodically checks for and removes channels that haven't been accessed in
+// 5 minutes
 func (c *channelMCPMpsc) cleanupExpiredChannels() {
 	ticker := time.NewTicker(1 * time.Minute)
 	defer ticker.Stop()

+ 7 - 1
core/controller/publicmcp.go

@@ -30,7 +30,13 @@ func GetPublicMCPs(c *gin.Context) {
 	keyword := c.Query("keyword")
 	status, _ := strconv.Atoi(c.Query("status"))
 
-	mcps, total, err := model.GetPublicMCPs(page, perPage, mcpType, keyword, model.PublicMCPStatus(status))
+	mcps, total, err := model.GetPublicMCPs(
+		page,
+		perPage,
+		mcpType,
+		keyword,
+		model.PublicMCPStatus(status),
+	)
 	if err != nil {
 		middleware.ErrorResponse(c, http.StatusInternalServerError, err.Error())
 		return

+ 100 - 20
core/controller/relay-controller.go

@@ -106,7 +106,11 @@ func updateChannelModelTokensRequestRate(c *gin.Context, meta *meta.Meta, tpm, t
 	log.Data["ch_tps"] = tps
 }
 
-func (w *wrapAdaptor) DoRequest(meta *meta.Meta, c *gin.Context, req *http.Request) (*http.Response, error) {
+func (w *wrapAdaptor) DoRequest(
+	meta *meta.Meta,
+	c *gin.Context,
+	req *http.Request,
+) (*http.Response, error) {
 	count, overLimitCount, secondCount := reqlimit.PushChannelModelRequest(
 		context.Background(),
 		strconv.Itoa(meta.Channel.ID),
@@ -116,7 +120,11 @@ func (w *wrapAdaptor) DoRequest(meta *meta.Meta, c *gin.Context, req *http.Reque
 	return w.Adaptor.DoRequest(meta, c, req)
 }
 
-func (w *wrapAdaptor) DoResponse(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model.Usage, adaptor.Error) {
+func (w *wrapAdaptor) DoResponse(
+	meta *meta.Meta,
+	c *gin.Context,
+	resp *http.Response,
+) (*model.Usage, adaptor.Error) {
 	usage, relayErr := w.Adaptor.DoResponse(meta, c, resp)
 	if usage == nil {
 		return nil, relayErr
@@ -218,7 +226,11 @@ func relayController(m mode.Mode) RelayController {
 	return c
 }
 
-func RelayHelper(c *gin.Context, meta *meta.Meta, handel RelayHandler) (*controller.HandleResult, bool) {
+func RelayHelper(
+	c *gin.Context,
+	meta *meta.Meta,
+	handel RelayHandler,
+) (*controller.HandleResult, bool) {
 	result := handel(c, meta)
 	if result.Error == nil {
 		if _, _, err := monitor.AddRequest(
@@ -251,7 +263,13 @@ func RelayHelper(c *gin.Context, meta *meta.Meta, handel RelayHandler) (*control
 		case banExecution:
 			notifyChannelIssue(c, meta, "autoBanned", "Auto Banned", result.Error)
 		case beyondThreshold:
-			notifyChannelIssue(c, meta, "beyondThreshold", "Error Rate Beyond Threshold", result.Error)
+			notifyChannelIssue(
+				c,
+				meta,
+				"beyondThreshold",
+				"Error Rate Beyond Threshold",
+				result.Error,
+			)
 		case !hasPermission:
 			notifyChannelIssue(c, meta, "channelHasPermission", "No Permission", result.Error)
 		}
@@ -259,17 +277,22 @@ func RelayHelper(c *gin.Context, meta *meta.Meta, handel RelayHandler) (*control
 	return result, shouldRetry
 }
 
-func notifyChannelIssue(c *gin.Context, meta *meta.Meta, issueType string, titleSuffix string, err adaptor.Error) {
-	var notifyFunc func(title string, message string)
+func notifyChannelIssue(
+	c *gin.Context,
+	meta *meta.Meta,
+	issueType, titleSuffix string,
+	err adaptor.Error,
+) {
+	var notifyFunc func(title, message string)
 
 	lockKey := fmt.Sprintf("%s:%d:%s", issueType, meta.Channel.ID, meta.OriginModel)
 	switch issueType {
 	case "beyondThreshold":
-		notifyFunc = func(title string, message string) {
+		notifyFunc = func(title, message string) {
 			notify.WarnThrottle(lockKey, time.Minute, title, message)
 		}
 	default:
-		notifyFunc = func(title string, message string) {
+		notifyFunc = func(title, message string) {
 			notify.ErrorThrottle(lockKey, time.Minute, title, message)
 		}
 	}
@@ -301,7 +324,13 @@ func notifyChannelIssue(c *gin.Context, meta *meta.Meta, issueType string, title
 		}
 
 		rate := getChannelModelRequestRate(c, meta)
-		message += fmt.Sprintf("\nrpm: %d\nrps: %d\ntpm: %d\ntps: %d", rate.RPM, rate.RPS, rate.TPM, rate.TPS)
+		message += fmt.Sprintf(
+			"\nrpm: %d\nrps: %d\ntpm: %d\ntps: %d",
+			rate.RPM,
+			rate.RPS,
+			rate.TPM,
+			rate.TPS,
+		)
 	}
 
 	notifyFunc(
@@ -329,7 +358,13 @@ var (
 	ErrChannelsExhausted = errors.New("channels exhausted")
 )
 
-func GetRandomChannel(mc *model.ModelCaches, availableSet []string, modelName string, errorRates map[int64]float64, ignoreChannel ...int64) (*model.Channel, []*model.Channel, error) {
+func GetRandomChannel(
+	mc *model.ModelCaches,
+	availableSet []string,
+	modelName string,
+	errorRates map[int64]float64,
+	ignoreChannel ...int64,
+) (*model.Channel, []*model.Channel, error) {
 	channelMap := make(map[int]*model.Channel)
 	if len(availableSet) != 0 {
 		for _, set := range availableSet {
@@ -362,8 +397,13 @@ func getPriority(channel *model.Channel, errorRate float64) int32 {
 	return int32(float64(priority) / errorRate)
 }
 
+//
 //nolint:gosec
-func getRandomChannel(channels []*model.Channel, errorRates map[int64]float64, ignoreChannel ...int64) (*model.Channel, error) {
+func getRandomChannel(
+	channels []*model.Channel,
+	errorRates map[int64]float64,
+	ignoreChannel ...int64,
+) (*model.Channel, error) {
 	if len(channels) == 0 {
 		return nil, ErrChannelsNotFound
 	}
@@ -400,8 +440,19 @@ func getRandomChannel(channels []*model.Channel, errorRates map[int64]float64, i
 	return channels[rand.IntN(len(channels))], nil
 }
 
-func getChannelWithFallback(cache *model.ModelCaches, availableSet []string, modelName string, errorRates map[int64]float64, ignoreChannelIDs ...int64) (*model.Channel, []*model.Channel, error) {
-	channel, migratedChannels, err := GetRandomChannel(cache, availableSet, modelName, errorRates, ignoreChannelIDs...)
+func getChannelWithFallback(
+	cache *model.ModelCaches,
+	availableSet []string,
+	modelName string,
+	errorRates map[int64]float64,
+	ignoreChannelIDs ...int64,
+) (*model.Channel, []*model.Channel, error) {
+	channel, migratedChannels, err := GetRandomChannel(
+		cache,
+		availableSet,
+		modelName,
+		errorRates,
+		ignoreChannelIDs...)
 	if err == nil {
 		return channel, migratedChannels, nil
 	}
@@ -419,7 +470,12 @@ func NewRelay(mode mode.Mode) func(c *gin.Context) {
 	}
 }
 
-func NewMetaByContext(c *gin.Context, channel *model.Channel, mode mode.Mode, opts ...meta.Option) *meta.Meta {
+func NewMetaByContext(
+	c *gin.Context,
+	channel *model.Channel,
+	mode mode.Mode,
+	opts ...meta.Option,
+) *meta.Meta {
 	return middleware.NewMetaByContext(c, channel, mode, opts...)
 }
 
@@ -615,7 +671,12 @@ func getInitialChannel(c *gin.Context, modelName string) (*initialChannel, error
 	group := middleware.GetGroup(c)
 	availableSet := group.GetAvailableSets()
 
-	channel, migratedChannels, err := getChannelWithFallback(mc, availableSet, modelName, errorRates, ids...)
+	channel, migratedChannels, err := getChannelWithFallback(
+		mc,
+		availableSet,
+		modelName,
+		errorRates,
+		ids...)
 	if err != nil {
 		return nil, err
 	}
@@ -651,7 +712,12 @@ func getWebSearchChannel(c *gin.Context, modelName string) (*model.Channel, erro
 	return channel, nil
 }
 
-func handleRelayResult(c *gin.Context, bizErr adaptor.Error, retry bool, retryTimes int) (done bool) {
+func handleRelayResult(
+	c *gin.Context,
+	bizErr adaptor.Error,
+	retry bool,
+	retryTimes int,
+) (done bool) {
 	if bizErr == nil {
 		return true
 	}
@@ -664,7 +730,13 @@ func handleRelayResult(c *gin.Context, bizErr adaptor.Error, retry bool, retryTi
 	return false
 }
 
-func initRetryState(retryTimes int, channel *initialChannel, meta *meta.Meta, result *controller.HandleResult, price model.Price) *retryState {
+func initRetryState(
+	retryTimes int,
+	channel *initialChannel,
+	meta *meta.Meta,
+	result *controller.HandleResult,
+	price model.Price,
+) *retryState {
 	state := &retryState{
 		retryTimes:       retryTimes,
 		ignoreChannelIDs: channel.ignoreChannelIDs,
@@ -792,7 +864,10 @@ func getRetryChannel(state *retryState) (*model.Channel, error) {
 		return state.lastHasPermissionChannel, nil
 	}
 
-	newChannel, err := getRandomChannel(state.migratedChannels, state.errorRates, state.ignoreChannelIDs...)
+	newChannel, err := getRandomChannel(
+		state.migratedChannels,
+		state.errorRates,
+		state.ignoreChannelIDs...)
 	if err != nil {
 		if !errors.Is(err, ErrChannelsExhausted) || state.lastHasPermissionChannel == nil {
 			return nil, err
@@ -813,7 +888,12 @@ func prepareRetry(c *gin.Context) error {
 	return nil
 }
 
-func handleRetryResult(ctx *gin.Context, retry bool, newChannel *model.Channel, state *retryState) (done bool) {
+func handleRetryResult(
+	ctx *gin.Context,
+	retry bool,
+	newChannel *model.Channel,
+	state *retryState,
+) (done bool) {
 	if ctx.Request.Context().Err() != nil {
 		return true
 	}
@@ -866,7 +946,7 @@ func channelHasPermission(relayErr adaptor.Error) bool {
 
 // shouldDelay checks if we need to add a delay before retrying
 // Only adds delay when retrying with the same channel for rate limiting issues
-func shouldDelay(statusCode int, lastChannelID, newChannelID int) bool {
+func shouldDelay(statusCode, lastChannelID, newChannelID int) bool {
 	if lastChannelID != newChannelID {
 		return false
 	}

+ 5 - 1
core/controller/relay-dashboard.go

@@ -30,7 +30,11 @@ func GetSubscription(c *gin.Context) {
 			return
 		}
 		log.Errorf("get group (%s) balance failed: %s", group.ID, err)
-		middleware.ErrorResponse(c, http.StatusInternalServerError, fmt.Sprintf("get group (%s) balance failed", group.ID))
+		middleware.ErrorResponse(
+			c,
+			http.StatusInternalServerError,
+			fmt.Sprintf("get group (%s) balance failed", group.ID),
+		)
 		return
 	}
 	token := middleware.GetToken(c)

+ 0 - 1
core/controller/relay.go

@@ -4,7 +4,6 @@ import (
 	"github.com/gin-gonic/gin"
 	"github.com/labring/aiproxy/core/middleware"
 	"github.com/labring/aiproxy/core/relay/mode"
-
 	// relay model used by swagger
 	_ "github.com/labring/aiproxy/core/relay/model"
 )

+ 20 - 2
core/controller/token.go

@@ -198,7 +198,16 @@ func SearchTokens(c *gin.Context) {
 	status, _ := strconv.Atoi(c.Query("status"))
 	group := c.Query("group")
 
-	tokens, total, err := model.SearchTokens(group, keyword, page, perPage, order, status, name, key)
+	tokens, total, err := model.SearchTokens(
+		group,
+		keyword,
+		page,
+		perPage,
+		order,
+		status,
+		name,
+		key,
+	)
 	if err != nil {
 		middleware.ErrorResponse(c, http.StatusInternalServerError, err.Error())
 		return
@@ -241,7 +250,16 @@ func SearchGroupTokens(c *gin.Context) {
 	key := c.Query("key")
 	status, _ := strconv.Atoi(c.Query("status"))
 
-	tokens, total, err := model.SearchGroupTokens(group, keyword, page, perPage, order, status, name, key)
+	tokens, total, err := model.SearchGroupTokens(
+		group,
+		keyword,
+		page,
+		perPage,
+		order,
+		status,
+		name,
+		key,
+	)
 	if err != nil {
 		middleware.ErrorResponse(c, http.StatusInternalServerError, err.Error())
 		return

+ 25 - 5
core/main.go

@@ -103,7 +103,7 @@ func setLog(l *log.Logger) {
 		FullTimestamp:    true,
 		TimestampFormat:  time.DateTime,
 		QuoteEmptyFields: true,
-		CallerPrettyfier: func(f *runtime.Frame) (function string, file string) {
+		CallerPrettyfier: func(f *runtime.Frame) (function, file string) {
 			if _, ok := logCallerIgnoreFuncs[f.Function]; ok {
 				return "", ""
 			}
@@ -206,18 +206,33 @@ func DetectIPGroups() {
 		slices.Sort(groups)
 		groupsJSON, err := sonic.MarshalString(groups)
 		if err != nil {
-			notify.ErrorThrottle("detectIPGroupsMarshal", time.Minute, "marshal IP groups failed", err.Error())
+			notify.ErrorThrottle(
+				"detectIPGroupsMarshal",
+				time.Minute,
+				"marshal IP groups failed",
+				err.Error(),
+			)
 			continue
 		}
 
 		if banThreshold >= threshold && len(groups) >= int(banThreshold) {
 			rowsAffected, err := model.UpdateGroupsStatus(groups, model.GroupStatusDisabled)
 			if err != nil {
-				notify.ErrorThrottle("detectIPGroupsBan", time.Minute, "update groups status failed", err.Error())
+				notify.ErrorThrottle(
+					"detectIPGroupsBan",
+					time.Minute,
+					"update groups status failed",
+					err.Error(),
+				)
 			}
 			if rowsAffected > 0 {
 				notify.Warn(
-					fmt.Sprintf("Suspicious activity: IP %s is using %d groups (exceeds ban threshold of %d). IP and all groups have been disabled.", ip, len(groups), banThreshold),
+					fmt.Sprintf(
+						"Suspicious activity: IP %s is using %d groups (exceeds ban threshold of %d). IP and all groups have been disabled.",
+						ip,
+						len(groups),
+						banThreshold,
+					),
 					groupsJSON,
 				)
 				ipblack.SetIPBlackAnyWay(ip, time.Hour*48)
@@ -233,7 +248,12 @@ func DetectIPGroups() {
 		notify.WarnThrottle(
 			hashKey,
 			time.Hour*3,
-			fmt.Sprintf("Potential abuse: IP %s is using %d groups (exceeds threshold of %d)", ip, len(groups), threshold),
+			fmt.Sprintf(
+				"Potential abuse: IP %s is using %d groups (exceeds threshold of %d)",
+				ip,
+				len(groups),
+				threshold,
+			),
 			groupsJSON,
 		)
 	}

+ 20 - 4
core/middleware/auth.go

@@ -145,15 +145,27 @@ func TokenAuth(c *gin.Context) {
 }
 
 func GetGroup(c *gin.Context) *model.GroupCache {
-	return c.MustGet(Group).(*model.GroupCache)
+	v, ok := c.MustGet(Group).(*model.GroupCache)
+	if !ok {
+		panic(fmt.Sprintf("group cache type error: %T, %v", v, v))
+	}
+	return v
 }
 
 func GetToken(c *gin.Context) *model.TokenCache {
-	return c.MustGet(Token).(*model.TokenCache)
+	v, ok := c.MustGet(Token).(*model.TokenCache)
+	if !ok {
+		panic(fmt.Sprintf("token cache type error: %T, %v", v, v))
+	}
+	return v
 }
 
 func GetModelCaches(c *gin.Context) *model.ModelCaches {
-	return c.MustGet(ModelCaches).(*model.ModelCaches)
+	v, ok := c.MustGet(ModelCaches).(*model.ModelCaches)
+	if !ok {
+		panic(fmt.Sprintf("model caches type error: %T, %v", v, v))
+	}
+	return v
 }
 
 func GetChannel(c *gin.Context) *model.Channel {
@@ -161,7 +173,11 @@ func GetChannel(c *gin.Context) *model.Channel {
 	if !exists {
 		return nil
 	}
-	return ch.(*model.Channel)
+	v, ok := ch.(*model.Channel)
+	if !ok {
+		panic(fmt.Sprintf("channel type error: %T, %v", v, v))
+	}
+	return v
 }
 
 func SetLogFieldsFromMeta(m *meta.Meta, fields logrus.Fields) {

+ 113 - 24
core/middleware/distributor.go

@@ -84,13 +84,13 @@ const (
 	XRateLimitResetTokens = "X-RateLimit-Reset-Tokens"
 )
 
-func setRpmHeaders(c *gin.Context, rpm int64, remainingRequests int64) {
+func setRpmHeaders(c *gin.Context, rpm, remainingRequests int64) {
 	c.Header(XRateLimitLimitRequests, strconv.FormatInt(rpm, 10))
 	c.Header(XRateLimitRemainingRequests, strconv.FormatInt(remainingRequests, 10))
 	c.Header(XRateLimitResetRequests, "1m0s")
 }
 
-func setTpmHeaders(c *gin.Context, tpm int64, remainingRequests int64) {
+func setTpmHeaders(c *gin.Context, tpm, remainingRequests int64) {
 	c.Header(XRateLimitLimitTokens, strconv.FormatInt(tpm, 10))
 	c.Header(XRateLimitRemainingTokens, strconv.FormatInt(remainingRequests, 10))
 	c.Header(XRateLimitResetTokens, "1m0s")
@@ -132,16 +132,40 @@ func UpdateGroupModelTokennameTokensRequest(c *gin.Context, tpm, tps int64) {
 	// log.Data["tps"] = strconv.FormatInt(tps, 10)
 }
 
-func checkGroupModelRPMAndTPM(c *gin.Context, group *model.GroupCache, mc model.ModelConfig, tokenName string) error {
+func checkGroupModelRPMAndTPM(
+	c *gin.Context,
+	group *model.GroupCache,
+	mc model.ModelConfig,
+	tokenName string,
+) error {
 	log := GetLogger(c)
 
 	adjustedModelConfig := GetGroupAdjustedModelConfig(group, mc)
 
-	groupModelCount, groupModelOverLimitCount, groupModelSecondCount := reqlimit.PushGroupModelRequest(c.Request.Context(), group.ID, mc.Model, adjustedModelConfig.RPM)
-	UpdateGroupModelRequest(c, group, groupModelCount+groupModelOverLimitCount, groupModelSecondCount)
+	groupModelCount, groupModelOverLimitCount, groupModelSecondCount := reqlimit.PushGroupModelRequest(
+		c.Request.Context(),
+		group.ID,
+		mc.Model,
+		adjustedModelConfig.RPM,
+	)
+	UpdateGroupModelRequest(
+		c,
+		group,
+		groupModelCount+groupModelOverLimitCount,
+		groupModelSecondCount,
+	)
 
-	groupModelTokenCount, groupModelTokenOverLimitCount, groupModelTokenSecondCount := reqlimit.PushGroupModelTokennameRequest(c.Request.Context(), group.ID, mc.Model, tokenName)
-	UpdateGroupModelTokennameRequest(c, groupModelTokenCount+groupModelTokenOverLimitCount, groupModelTokenSecondCount)
+	groupModelTokenCount, groupModelTokenOverLimitCount, groupModelTokenSecondCount := reqlimit.PushGroupModelTokennameRequest(
+		c.Request.Context(),
+		group.ID,
+		mc.Model,
+		tokenName,
+	)
+	UpdateGroupModelTokennameRequest(
+		c,
+		groupModelTokenCount+groupModelTokenOverLimitCount,
+		groupModelTokenSecondCount,
+	)
 
 	if group.Status != model.GroupStatusInternal &&
 		adjustedModelConfig.RPM > 0 {
@@ -153,10 +177,19 @@ func checkGroupModelRPMAndTPM(c *gin.Context, group *model.GroupCache, mc model.
 		setRpmHeaders(c, adjustedModelConfig.RPM, adjustedModelConfig.RPM-groupModelCount)
 	}
 
-	groupModelCountTPM, groupModelCountTPS := reqlimit.GetGroupModelTokensRequest(c.Request.Context(), group.ID, mc.Model)
+	groupModelCountTPM, groupModelCountTPS := reqlimit.GetGroupModelTokensRequest(
+		c.Request.Context(),
+		group.ID,
+		mc.Model,
+	)
 	UpdateGroupModelTokensRequest(c, group, groupModelCountTPM, groupModelCountTPS)
 
-	groupModelTokenCountTPM, groupModelTokenCountTPS := reqlimit.GetGroupModelTokennameTokensRequest(c.Request.Context(), group.ID, mc.Model, tokenName)
+	groupModelTokenCountTPM, groupModelTokenCountTPS := reqlimit.GetGroupModelTokennameTokensRequest(
+		c.Request.Context(),
+		group.ID,
+		mc.Model,
+		tokenName,
+	)
 	UpdateGroupModelTokennameTokensRequest(c, groupModelTokenCountTPM, groupModelTokenCountTPS)
 
 	if group.Status != model.GroupStatusInternal &&
@@ -191,7 +224,10 @@ func GetGroupBalanceConsumerFromContext(c *gin.Context) *GroupBalanceConsumer {
 	return nil
 }
 
-func GetGroupBalanceConsumer(c *gin.Context, group *model.GroupCache) (*GroupBalanceConsumer, error) {
+func GetGroupBalanceConsumer(
+	c *gin.Context,
+	group *model.GroupCache,
+) (*GroupBalanceConsumer, error) {
 	gbc := GetGroupBalanceConsumerFromContext(c)
 	if gbc != nil {
 		return gbc, nil
@@ -235,7 +271,12 @@ func checkGroupBalance(c *gin.Context, group *model.GroupCache) bool {
 	gbc, err := GetGroupBalanceConsumer(c, group)
 	if err != nil {
 		if errors.Is(err, balance.ErrNoRealNameUsedAmountLimit) {
-			AbortLogWithMessage(c, http.StatusForbidden, err.Error(), "no_real_name_used_amount_limit")
+			AbortLogWithMessage(
+				c,
+				http.StatusForbidden,
+				err.Error(),
+				"no_real_name_used_amount_limit",
+			)
 			return false
 		}
 		notify.ErrorThrottle(
@@ -244,7 +285,12 @@ func checkGroupBalance(c *gin.Context, group *model.GroupCache) bool {
 			fmt.Sprintf("Get group `%s` balance error", group.ID),
 			err.Error(),
 		)
-		AbortWithMessage(c, http.StatusInternalServerError, fmt.Sprintf("get group `%s` balance error", group.ID), "get_group_balance_error")
+		AbortWithMessage(
+			c,
+			http.StatusInternalServerError,
+			fmt.Sprintf("get group `%s` balance error", group.ID),
+			"get_group_balance_error",
+		)
 		return false
 	}
 
@@ -255,12 +301,21 @@ func checkGroupBalance(c *gin.Context, group *model.GroupCache) bool {
 			"groupBalanceAlert:"+group.ID,
 			time.Minute*15,
 			fmt.Sprintf("Group `%s` balance below threshold", group.ID),
-			fmt.Sprintf("Group `%s` balance has fallen below the threshold\nCurrent balance: %.2f", group.ID, gbc.balance),
+			fmt.Sprintf(
+				"Group `%s` balance has fallen below the threshold\nCurrent balance: %.2f",
+				group.ID,
+				gbc.balance,
+			),
 		)
 	}
 
 	if !gbc.CheckBalance(0) {
-		AbortLogWithMessage(c, http.StatusForbidden, fmt.Sprintf("group `%s` balance not enough", group.ID), GroupBalanceNotEnough)
+		AbortLogWithMessage(
+			c,
+			http.StatusForbidden,
+			fmt.Sprintf("group `%s` balance not enough", group.ID),
+			GroupBalanceNotEnough,
+		)
 		return false
 	}
 	return true
@@ -276,7 +331,12 @@ const (
 	AIProxyChannelHeader = "Aiproxy-Channel"
 )
 
-func getChannelFromHeader(header string, mc *model.ModelCaches, availableSet []string, model string) (*model.Channel, error) {
+func getChannelFromHeader(
+	header string,
+	mc *model.ModelCaches,
+	availableSet []string,
+	model string,
+) (*model.Channel, error) {
 	channelIDInt, err := strconv.ParseInt(header, 10, 64)
 	if err != nil {
 		return nil, err
@@ -305,7 +365,7 @@ func getChannelFromHeader(header string, mc *model.ModelCaches, availableSet []s
 	return nil, fmt.Errorf("channel %d not found for model `%s`", channelIDInt, model)
 }
 
-func CheckRelayMode(requestMode mode.Mode, modelMode mode.Mode) bool {
+func CheckRelayMode(requestMode, modelMode mode.Mode) bool {
 	if modelMode == mode.Unknown {
 		return true
 	}
@@ -340,7 +400,12 @@ func distribute(c *gin.Context, mode mode.Mode) {
 
 	requestModel, err := getRequestModel(c, mode)
 	if err != nil {
-		AbortLogWithMessage(c, http.StatusInternalServerError, err.Error(), "get_request_model_error")
+		AbortLogWithMessage(
+			c,
+			http.StatusInternalServerError,
+			err.Error(),
+			"get_request_model_error",
+		)
 		return
 	}
 	if requestModel == "" {
@@ -354,17 +419,27 @@ func distribute(c *gin.Context, mode mode.Mode) {
 
 	mc, ok := GetModelCaches(c).ModelConfig.GetModelConfig(requestModel)
 	if !ok || !CheckRelayMode(mode, mc.Type) {
-		AbortLogWithMessage(c,
+		AbortLogWithMessage(
+			c,
 			http.StatusNotFound,
-			fmt.Sprintf("The model `%s` does not exist or you do not have access to it.", requestModel),
+			fmt.Sprintf(
+				"The model `%s` does not exist or you do not have access to it.",
+				requestModel,
+			),
 			"model_not_found",
 		)
 		return
 	}
 	c.Set(ModelConfig, mc)
 
-	if channelHeader := c.Request.Header.Get(AIProxyChannelHeader); group.Status == model.GroupStatusInternal && channelHeader != "" {
-		channel, err := getChannelFromHeader(channelHeader, GetModelCaches(c), group.GetAvailableSets(), requestModel)
+	if channelHeader := c.Request.Header.Get(AIProxyChannelHeader); group.Status == model.GroupStatusInternal &&
+		channelHeader != "" {
+		channel, err := getChannelFromHeader(
+			channelHeader,
+			GetModelCaches(c),
+			group.GetAvailableSets(),
+			requestModel,
+		)
 		if err != nil {
 			AbortLogWithMessage(c, http.StatusBadRequest, err.Error())
 			return
@@ -384,14 +459,24 @@ func distribute(c *gin.Context, mode mode.Mode) {
 
 	user, err := getRequestUser(c, mode)
 	if err != nil {
-		AbortLogWithMessage(c, http.StatusInternalServerError, err.Error(), "get_request_user_error")
+		AbortLogWithMessage(
+			c,
+			http.StatusInternalServerError,
+			err.Error(),
+			"get_request_user_error",
+		)
 		return
 	}
 	c.Set(RequestUser, user)
 
 	metadata, err := getRequestMetadata(c, mode)
 	if err != nil {
-		AbortLogWithMessage(c, http.StatusInternalServerError, err.Error(), "get_request_metadata_error")
+		AbortLogWithMessage(
+			c,
+			http.StatusInternalServerError,
+			err.Error(),
+			"get_request_metadata_error",
+		)
 		return
 	}
 	c.Set(RequestMetadata, metadata)
@@ -462,7 +547,11 @@ func GetRequestMetadata(c *gin.Context) map[string]string {
 }
 
 func GetModelConfig(c *gin.Context) model.ModelConfig {
-	return c.MustGet(ModelConfig).(model.ModelConfig)
+	v, ok := c.MustGet(ModelConfig).(model.ModelConfig)
+	if !ok {
+		panic(fmt.Sprintf("model config type error: %T, %v", v, v))
+	}
+	return v
 }
 
 func NewMetaByContext(c *gin.Context,

+ 17 - 5
core/middleware/distributor_test.go

@@ -10,7 +10,7 @@ import (
 )
 
 type ModelRequest struct {
-	Model string `form:"model" json:"model"`
+	Model string `json:"model" form:"model"`
 }
 
 func StdGetModelFromJSON(body []byte) (string, error) {
@@ -45,19 +45,31 @@ func BenchmarkCompareGetModelFromJSON(b *testing.B) {
 		},
 		{
 			name: "LargeJSON",
-			json: `{"model": "gpt-4","messages": [{"role": "user", "content": "` + strings.Repeat("x", 1000) + `"}]}`,
+			json: `{"model": "gpt-4","messages": [{"role": "user", "content": "` + strings.Repeat(
+				"x",
+				1000,
+			) + `"}]}`,
 		},
 		{
 			name: "LargeJSON2",
-			json: `{"messages": [{"role": "user", "content": "` + strings.Repeat("x", 1000) + `"}],"model": "gpt-4"}`,
+			json: `{"messages": [{"role": "user", "content": "` + strings.Repeat(
+				"x",
+				1000,
+			) + `"}],"model": "gpt-4"}`,
 		},
 		{
 			name: "VeryLargeJSON",
-			json: `{"model": "gpt-4","messages": [{"role": "user", "content": "` + strings.Repeat("x", 10000) + `"}]}`,
+			json: `{"model": "gpt-4","messages": [{"role": "user", "content": "` + strings.Repeat(
+				"x",
+				10000,
+			) + `"}]}`,
 		},
 		{
 			name: "VeryLargeJSON2",
-			json: `{"messages": [{"role": "user", "content": "` + strings.Repeat("x", 10000) + `"}],"model": "gpt-4"}`,
+			json: `{"messages": [{"role": "user", "content": "` + strings.Repeat(
+				"x",
+				10000,
+			) + `"}],"model": "gpt-4"}`,
 		},
 	}
 

+ 14 - 3
core/middleware/log.go

@@ -18,7 +18,10 @@ var fieldsPool = sync.Pool{
 
 func NewLog(l *logrus.Logger) gin.HandlerFunc {
 	return func(c *gin.Context) {
-		fields := fieldsPool.Get().(logrus.Fields)
+		fields, ok := fieldsPool.Get().(logrus.Fields)
+		if !ok {
+			panic(fmt.Sprintf("fields pool type error: %T, %v", fields, fields))
+		}
 		defer func() {
 			clear(fields)
 			fieldsPool.Put(fields)
@@ -95,7 +98,11 @@ func formatter(param gin.LogFormatterParams) string {
 
 func GetLogger(c *gin.Context) *logrus.Entry {
 	if log, ok := c.Get("log"); ok {
-		return log.(*logrus.Entry)
+		v, ok := log.(*logrus.Entry)
+		if !ok {
+			panic(fmt.Sprintf("log type error: %T, %v", v, v))
+		}
+		return v
 	}
 	entry := NewLogger()
 	c.Set("log", entry)
@@ -103,8 +110,12 @@ func GetLogger(c *gin.Context) *logrus.Entry {
 }
 
 func NewLogger() *logrus.Entry {
+	fields, ok := fieldsPool.Get().(logrus.Fields)
+	if !ok {
+		panic(fmt.Sprintf("fields pool type error: %T, %v", fields, fields))
+	}
 	return &logrus.Entry{
 		Logger: logrus.StandardLogger(),
-		Data:   fieldsPool.Get().(logrus.Fields),
+		Data:   fields,
 	}
 }

+ 21 - 3
core/middleware/utils.go

@@ -1,17 +1,31 @@
 package middleware
 
 import (
+	"fmt"
+
 	"github.com/gin-gonic/gin"
 	"github.com/labring/aiproxy/core/relay/mode"
 	relaymodel "github.com/labring/aiproxy/core/relay/model"
 )
 
-func AbortLogWithMessageWithMode(m mode.Mode, c *gin.Context, statusCode int, message string, typ ...string) {
+func AbortLogWithMessageWithMode(
+	m mode.Mode,
+	c *gin.Context,
+	statusCode int,
+	message string,
+	typ ...string,
+) {
 	GetLogger(c).Error(message)
 	AbortWithMessageWithMode(m, c, statusCode, message, typ...)
 }
 
-func AbortWithMessageWithMode(m mode.Mode, c *gin.Context, statusCode int, message string, typ ...string) {
+func AbortWithMessageWithMode(
+	m mode.Mode,
+	c *gin.Context,
+	statusCode int,
+	message string,
+	typ ...string,
+) {
 	c.JSON(statusCode,
 		relaymodel.WrapperErrorWithMessage(m, statusCode, message, typ...),
 	)
@@ -35,5 +49,9 @@ func GetMode(c *gin.Context) mode.Mode {
 	if !exists {
 		return mode.Unknown
 	}
-	return m.(mode.Mode)
+	v, ok := m.(mode.Mode)
+	if !ok {
+		panic(fmt.Sprintf("mode type error: %T, %v", v, v))
+	}
+	return v
 }

+ 39 - 5
core/model/batch.go

@@ -66,7 +66,13 @@ type GroupSummaryUpdate struct {
 }
 
 func groupSummaryUniqueKey(unique GroupSummaryUnique) string {
-	return fmt.Sprintf("%s:%s:%s:%d", unique.GroupID, unique.TokenName, unique.Model, unique.HourTimestamp)
+	return fmt.Sprintf(
+		"%s:%s:%s:%d",
+		unique.GroupID,
+		unique.TokenName,
+		unique.Model,
+		unique.HourTimestamp,
+	)
 }
 
 var batchData batchUpdateData
@@ -141,7 +147,11 @@ func ProcessBatchUpdatesSummary() {
 func processGroupUpdates(wg *sync.WaitGroup) {
 	defer wg.Done()
 	for groupID, data := range batchData.Groups {
-		err := UpdateGroupUsedAmountAndRequestCount(groupID, data.Amount.InexactFloat64(), data.Count)
+		err := UpdateGroupUsedAmountAndRequestCount(
+			groupID,
+			data.Amount.InexactFloat64(),
+			data.Count,
+		)
 		if IgnoreNotFound(err) != nil {
 			notify.ErrorThrottle(
 				"batchUpdateGroupUsedAmountAndRequestCount",
@@ -322,7 +332,16 @@ func BatchRecordLogs(
 		updateSummaryData(channelID, modelName, now, code, amountDecimal, usage, channelModelRate)
 	}
 
-	updateGroupSummaryData(group, tokenName, modelName, now, code, amountDecimal, usage, groupModelTokenRate)
+	updateGroupSummaryData(
+		group,
+		tokenName,
+		modelName,
+		now,
+		code,
+		amountDecimal,
+		usage,
+		groupModelTokenRate,
+	)
 
 	return err
 }
@@ -369,7 +388,14 @@ func updateTokenData(tokenID int, amount float64, amountDecimal decimal.Decimal)
 	}
 }
 
-func updateGroupSummaryData(group string, tokenName string, modelName string, createAt time.Time, code int, amountDecimal decimal.Decimal, usage Usage, groupModelTokenRate RequestRate) {
+func updateGroupSummaryData(
+	group, tokenName, modelName string,
+	createAt time.Time,
+	code int,
+	amountDecimal decimal.Decimal,
+	usage Usage,
+	groupModelTokenRate RequestRate,
+) {
 	groupUnique := GroupSummaryUnique{
 		GroupID:       group,
 		TokenName:     tokenName,
@@ -410,7 +436,15 @@ func updateGroupSummaryData(group string, tokenName string, modelName string, cr
 	}
 }
 
-func updateSummaryData(channelID int, modelName string, createAt time.Time, code int, amountDecimal decimal.Decimal, usage Usage, channelModelRate RequestRate) {
+func updateSummaryData(
+	channelID int,
+	modelName string,
+	createAt time.Time,
+	code int,
+	amountDecimal decimal.Decimal,
+	usage Usage,
+	channelModelRate RequestRate,
+) {
 	summaryUnique := SummaryUnique{
 		ChannelID:     channelID,
 		Model:         modelName,

+ 38 - 14
core/model/cache.go

@@ -211,7 +211,8 @@ func CacheUpdateTokenUsedAmountOnlyIncrease(key string, amount float64) error {
 	if !common.RedisEnabled {
 		return nil
 	}
-	return updateTokenUsedAmountOnlyIncreaseScript.Run(context.Background(), common.RDB, []string{fmt.Sprintf(TokenCacheKey, key)}, amount).Err()
+	return updateTokenUsedAmountOnlyIncreaseScript.Run(context.Background(), common.RDB, []string{fmt.Sprintf(TokenCacheKey, key)}, amount).
+		Err()
 }
 
 var updateTokenNameScript = redis.NewScript(`
@@ -221,11 +222,12 @@ var updateTokenNameScript = redis.NewScript(`
 	return redis.status_reply("ok")
 `)
 
-func CacheUpdateTokenName(key string, name string) error {
+func CacheUpdateTokenName(key, name string) error {
 	if !common.RedisEnabled {
 		return nil
 	}
-	return updateTokenNameScript.Run(context.Background(), common.RDB, []string{fmt.Sprintf(TokenCacheKey, key)}, name).Err()
+	return updateTokenNameScript.Run(context.Background(), common.RDB, []string{fmt.Sprintf(TokenCacheKey, key)}, name).
+		Err()
 }
 
 var updateTokenStatusScript = redis.NewScript(`
@@ -239,7 +241,8 @@ func CacheUpdateTokenStatus(key string, status int) error {
 	if !common.RedisEnabled {
 		return nil
 	}
-	return updateTokenStatusScript.Run(context.Background(), common.RDB, []string{fmt.Sprintf(TokenCacheKey, key)}, status).Err()
+	return updateTokenStatusScript.Run(context.Background(), common.RDB, []string{fmt.Sprintf(TokenCacheKey, key)}, status).
+		Err()
 }
 
 type redisMap[K comparable, V any] map[K]V
@@ -318,7 +321,8 @@ func CacheUpdateGroupRPMRatio(id string, rpmRatio float64) error {
 	if !common.RedisEnabled {
 		return nil
 	}
-	return updateGroupRPMRatioScript.Run(context.Background(), common.RDB, []string{fmt.Sprintf(GroupCacheKey, id)}, rpmRatio).Err()
+	return updateGroupRPMRatioScript.Run(context.Background(), common.RDB, []string{fmt.Sprintf(GroupCacheKey, id)}, rpmRatio).
+		Err()
 }
 
 var updateGroupTPMRatioScript = redis.NewScript(`
@@ -332,7 +336,8 @@ func CacheUpdateGroupTPMRatio(id string, tpmRatio float64) error {
 	if !common.RedisEnabled {
 		return nil
 	}
-	return updateGroupTPMRatioScript.Run(context.Background(), common.RDB, []string{fmt.Sprintf(GroupCacheKey, id)}, tpmRatio).Err()
+	return updateGroupTPMRatioScript.Run(context.Background(), common.RDB, []string{fmt.Sprintf(GroupCacheKey, id)}, tpmRatio).
+		Err()
 }
 
 var updateGroupStatusScript = redis.NewScript(`
@@ -346,7 +351,8 @@ func CacheUpdateGroupStatus(id string, status int) error {
 	if !common.RedisEnabled {
 		return nil
 	}
-	return updateGroupStatusScript.Run(context.Background(), common.RDB, []string{fmt.Sprintf(GroupCacheKey, id)}, status).Err()
+	return updateGroupStatusScript.Run(context.Background(), common.RDB, []string{fmt.Sprintf(GroupCacheKey, id)}, status).
+		Err()
 }
 
 //nolint:gosec
@@ -412,7 +418,8 @@ func CacheUpdateGroupUsedAmountOnlyIncrease(id string, amount float64) error {
 	if !common.RedisEnabled {
 		return nil
 	}
-	return updateGroupUsedAmountOnlyIncreaseScript.Run(context.Background(), common.RDB, []string{fmt.Sprintf(GroupCacheKey, id)}, amount).Err()
+	return updateGroupUsedAmountOnlyIncreaseScript.Run(context.Background(), common.RDB, []string{fmt.Sprintf(GroupCacheKey, id)}, amount).
+		Err()
 }
 
 type GroupMCPCache struct {
@@ -503,7 +510,8 @@ func CacheUpdateGroupMCPStatus(groupID, mcpID string, status GroupMCPStatus) err
 	if !common.RedisEnabled {
 		return nil
 	}
-	return updateGroupMCPStatusScript.Run(context.Background(), common.RDB, []string{fmt.Sprintf(GroupMCPCacheKey, groupID, mcpID)}, status).Err()
+	return updateGroupMCPStatusScript.Run(context.Background(), common.RDB, []string{fmt.Sprintf(GroupMCPCacheKey, groupID, mcpID)}, status).
+		Err()
 }
 
 type PublicMCPCache struct {
@@ -596,7 +604,8 @@ func CacheUpdatePublicMCPStatus(mcpID string, status PublicMCPStatus) error {
 	if !common.RedisEnabled {
 		return nil
 	}
-	return updatePublicMCPStatusScript.Run(context.Background(), common.RDB, []string{fmt.Sprintf(PublicMCPCacheKey, mcpID)}, status).Err()
+	return updatePublicMCPStatusScript.Run(context.Background(), common.RDB, []string{fmt.Sprintf(PublicMCPCacheKey, mcpID)}, status).
+		Err()
 }
 
 const (
@@ -724,7 +733,10 @@ func InitModelConfigAndChannelCache() error {
 	sortChannelsByPriorityBySet(enabledModel2ChannelsBySet)
 
 	// Build enabled models and configs by set
-	enabledModelsBySet, enabledModelConfigsBySet, enabledModelConfigsMap := buildEnabledModelsBySet(enabledModel2ChannelsBySet, modelConfig)
+	enabledModelsBySet, enabledModelConfigsBySet, enabledModelConfigsMap := buildEnabledModelsBySet(
+		enabledModel2ChannelsBySet,
+		modelConfig,
+	)
 
 	// Load disabled channels
 	disabledChannels, err := LoadDisabledChannels()
@@ -903,7 +915,10 @@ func sortChannelsByPriorityBySet(modelMapBySet map[string]map[string][]*Channel)
 	}
 }
 
-func buildEnabledModelsBySet(modelMapBySet map[string]map[string][]*Channel, modelConfigCache ModelConfigCache) (
+func buildEnabledModelsBySet(
+	modelMapBySet map[string]map[string][]*Channel,
+	modelConfigCache ModelConfigCache,
+) (
 	map[string][]string,
 	map[string][]ModelConfig,
 	map[string]ModelConfig,
@@ -962,7 +977,11 @@ func SortModelConfigsFunc(i, j ModelConfig) int {
 	return 1
 }
 
-func SyncModelConfigAndChannelCache(ctx context.Context, wg *sync.WaitGroup, frequency time.Duration) {
+func SyncModelConfigAndChannelCache(
+	ctx context.Context,
+	wg *sync.WaitGroup,
+	frequency time.Duration,
+) {
 	defer wg.Done()
 
 	ticker := time.NewTicker(frequency)
@@ -974,7 +993,12 @@ func SyncModelConfigAndChannelCache(ctx context.Context, wg *sync.WaitGroup, fre
 		case <-ticker.C:
 			err := InitModelConfigAndChannelCache()
 			if err != nil {
-				notify.ErrorThrottle("syncModelChannel", time.Minute, "failed to sync channels", err.Error())
+				notify.ErrorThrottle(
+					"syncModelChannel",
+					time.Minute,
+					"failed to sync channels",
+					err.Error(),
+				)
 			}
 		}
 	}

+ 45 - 13
core/model/channel.go

@@ -69,23 +69,23 @@ func (c *ChannelConfig) Get(key ...any) (ast.Node, error) {
 type Channel struct {
 	DeletedAt               gorm.DeletedAt    `gorm:"index"                              json:"-"`
 	CreatedAt               time.Time         `gorm:"index"                              json:"created_at"`
-	LastTestErrorAt         time.Time         `json:"last_test_error_at"`
+	LastTestErrorAt         time.Time         `                                          json:"last_test_error_at"`
 	ChannelTests            []*ChannelTest    `gorm:"foreignKey:ChannelID;references:ID" json:"channel_tests,omitempty"`
-	BalanceUpdatedAt        time.Time         `json:"balance_updated_at"`
+	BalanceUpdatedAt        time.Time         `                                          json:"balance_updated_at"`
 	ModelMapping            map[string]string `gorm:"serializer:fastjson;type:text"      json:"model_mapping"`
 	Key                     string            `gorm:"type:text;index"                    json:"key"`
 	Name                    string            `gorm:"index"                              json:"name"`
 	BaseURL                 string            `gorm:"index"                              json:"base_url"`
 	Models                  []string          `gorm:"serializer:fastjson;type:text"      json:"models"`
-	Balance                 float64           `json:"balance"`
+	Balance                 float64           `                                          json:"balance"`
 	ID                      int               `gorm:"primaryKey"                         json:"id"`
 	UsedAmount              float64           `gorm:"index"                              json:"used_amount"`
 	RequestCount            int               `gorm:"index"                              json:"request_count"`
 	Status                  int               `gorm:"default:1;index"                    json:"status"`
 	Type                    ChannelType       `gorm:"default:0;index"                    json:"type"`
-	Priority                int32             `json:"priority"`
-	EnabledAutoBalanceCheck bool              `json:"enabled_auto_balance_check"`
-	BalanceThreshold        float64           `json:"balance_threshold"`
+	Priority                int32             `                                          json:"priority"`
+	EnabledAutoBalanceCheck bool              `                                          json:"enabled_auto_balance_check"`
+	BalanceThreshold        float64           `                                          json:"balance_threshold"`
 	Config                  *ChannelConfig    `gorm:"serializer:fastjson;type:text"      json:"config,omitempty"`
 	Sets                    []string          `gorm:"serializer:fastjson;type:text"      json:"sets,omitempty"`
 }
@@ -190,7 +190,16 @@ func (c *Channel) MarshalJSON() ([]byte, error) {
 func getChannelOrder(order string) string {
 	prefix, suffix, _ := strings.Cut(order, "-")
 	switch prefix {
-	case "name", "type", "created_at", "status", "test_at", "balance_updated_at", "used_amount", "request_count", "priority", "id":
+	case "name",
+		"type",
+		"created_at",
+		"status",
+		"test_at",
+		"balance_updated_at",
+		"used_amount",
+		"request_count",
+		"priority",
+		"id":
 		switch suffix {
 		case "asc":
 			return prefix + " asc"
@@ -208,7 +217,12 @@ func GetAllChannels() (channels []*Channel, err error) {
 	return channels, err
 }
 
-func GetChannels(page int, perPage int, id int, name string, key string, channelType int, baseURL string, order string) (channels []*Channel, total int64, err error) {
+func GetChannels(
+	page, perPage, id int,
+	name, key string,
+	channelType int,
+	baseURL, order string,
+) (channels []*Channel, total int64, err error) {
 	tx := DB.Model(&Channel{})
 	if id != 0 {
 		tx = tx.Where("id = ?", id)
@@ -237,7 +251,13 @@ func GetChannels(page int, perPage int, id int, name string, key string, channel
 	return channels, total, err
 }
 
-func SearchChannels(keyword string, page int, perPage int, id int, name string, key string, channelType int, baseURL string, order string) (channels []*Channel, total int64, err error) {
+func SearchChannels(
+	keyword string,
+	page, perPage, id int,
+	name, key string,
+	channelType int,
+	baseURL, order string,
+) (channels []*Channel, total int64, err error) {
 	tx := DB.Model(&Channel{})
 
 	// Handle exact match conditions for non-zero values
@@ -384,15 +404,27 @@ func UpdateChannel(channel *Channel) (err error) {
 }
 
 func ClearLastTestErrorAt(id int) error {
-	result := DB.Model(&Channel{}).Where("id = ?", id).Update("last_test_error_at", gorm.Expr("NULL"))
+	result := DB.Model(&Channel{}).
+		Where("id = ?", id).
+		Update("last_test_error_at", gorm.Expr("NULL"))
 	return HandleUpdateResult(result, ErrChannelNotFound)
 }
 
-func (c *Channel) UpdateModelTest(testAt time.Time, model, actualModel string, mode mode.Mode, took float64, success bool, response string, code int) (*ChannelTest, error) {
+func (c *Channel) UpdateModelTest(
+	testAt time.Time,
+	model, actualModel string,
+	mode mode.Mode,
+	took float64,
+	success bool,
+	response string,
+	code int,
+) (*ChannelTest, error) {
 	var ct *ChannelTest
 	err := DB.Transaction(func(tx *gorm.DB) error {
 		if !success {
-			result := tx.Model(&Channel{}).Where("id = ?", c.ID).Update("last_test_error_at", testAt)
+			result := tx.Model(&Channel{}).
+				Where("id = ?", c.ID).
+				Update("last_test_error_at", testAt)
 			if err := HandleUpdateResult(result, ErrChannelNotFound); err != nil {
 				return err
 			}
@@ -463,7 +495,7 @@ func DeleteChannelsByIDs(ids []int) (err error) {
 	})
 }
 
-func UpdateChannelStatusByID(id int, status int) error {
+func UpdateChannelStatusByID(id, status int) error {
 	result := DB.Model(&Channel{}).
 		Where("id = ?", id).
 		Update("status", status)

+ 3 - 3
core/model/channeltest.go

@@ -9,12 +9,12 @@ import (
 
 type ChannelTest struct {
 	TestAt      time.Time   `json:"test_at"`
-	Model       string      `gorm:"primaryKey"   json:"model"`
+	Model       string      `json:"model"        gorm:"primaryKey"`
 	ActualModel string      `json:"actual_model"`
-	Response    string      `gorm:"type:text"    json:"response"`
+	Response    string      `json:"response"     gorm:"type:text"`
 	ChannelName string      `json:"channel_name"`
 	ChannelType ChannelType `json:"channel_type"`
-	ChannelID   int         `gorm:"primaryKey"   json:"channel_id"`
+	ChannelID   int         `json:"channel_id"   gorm:"primaryKey"`
 	Took        float64     `json:"took"`
 	Success     bool        `json:"success"`
 	Mode        mode.Mode   `json:"mode"`

+ 16 - 6
core/model/consumeerr.go

@@ -11,15 +11,15 @@ import (
 
 type ConsumeError struct {
 	RequestAt  time.Time       `gorm:"index;index:idx_consume_error_group_reqat,priority:2" json:"request_at"`
-	CreatedAt  time.Time       `json:"created_at"`
+	CreatedAt  time.Time       `                                                            json:"created_at"`
 	GroupID    string          `gorm:"index;index:idx_consume_error_group_reqat,priority:1" json:"group_id"`
 	RequestID  string          `gorm:"index"                                                json:"request_id"`
 	TokenName  EmptyNullString `gorm:"not null"                                             json:"token_name"`
-	Model      string          `json:"model"`
+	Model      string          `                                                            json:"model"`
 	Content    string          `gorm:"type:text"                                            json:"content"`
 	ID         int             `gorm:"primaryKey"                                           json:"id"`
-	UsedAmount float64         `json:"used_amount"`
-	TokenID    int             `json:"token_id"`
+	UsedAmount float64         `                                                            json:"used_amount"`
+	TokenID    int             `                                                            json:"token_id"`
 }
 
 func (c *ConsumeError) MarshalJSON() ([]byte, error) {
@@ -35,7 +35,13 @@ func (c *ConsumeError) MarshalJSON() ([]byte, error) {
 	})
 }
 
-func CreateConsumeError(requestID string, requestAt time.Time, group string, tokenName string, model string, content string, usedAmount float64, tokenID int) error {
+func CreateConsumeError(
+	requestID string,
+	requestAt time.Time,
+	group, tokenName, model, content string,
+	usedAmount float64,
+	tokenID int,
+) error {
 	return LogDB.Create(&ConsumeError{
 		RequestID:  requestID,
 		RequestAt:  requestAt,
@@ -48,7 +54,11 @@ func CreateConsumeError(requestID string, requestAt time.Time, group string, tok
 	}).Error
 }
 
-func SearchConsumeError(keyword string, requestID string, group string, tokenName string, model string, tokenID int, page int, perPage int, order string) ([]*ConsumeError, int64, error) {
+func SearchConsumeError(
+	keyword, requestID, group, tokenName, model string,
+	tokenID, page, perPage int,
+	order string,
+) ([]*ConsumeError, int64, error) {
 	tx := LogDB.Model(&ConsumeError{})
 
 	// Handle exact match conditions for non-zero values

+ 33 - 16
core/model/group.go

@@ -23,17 +23,17 @@ const (
 
 type Group struct {
 	CreatedAt              time.Time               `json:"created_at"`
-	ID                     string                  `gorm:"primaryKey"                    json:"id"`
-	Tokens                 []Token                 `gorm:"foreignKey:GroupID"            json:"-"`
-	GroupModelConfigs      []GroupModelConfig      `gorm:"foreignKey:GroupID"            json:"-"`
-	PublicMCPReusingParams []PublicMCPReusingParam `gorm:"foreignKey:GroupID"            json:"-"`
-	GroupMCPs              []GroupMCP              `gorm:"foreignKey:GroupID"            json:"-"`
-	Status                 int                     `gorm:"default:1;index"               json:"status"`
-	RPMRatio               float64                 `gorm:"index"                         json:"rpm_ratio,omitempty"`
-	TPMRatio               float64                 `gorm:"index"                         json:"tpm_ratio,omitempty"`
-	UsedAmount             float64                 `gorm:"index"                         json:"used_amount"`
-	RequestCount           int                     `gorm:"index"                         json:"request_count"`
-	AvailableSets          []string                `gorm:"serializer:fastjson;type:text" json:"available_sets,omitempty"`
+	ID                     string                  `json:"id"                       gorm:"primaryKey"`
+	Tokens                 []Token                 `json:"-"                        gorm:"foreignKey:GroupID"`
+	GroupModelConfigs      []GroupModelConfig      `json:"-"                        gorm:"foreignKey:GroupID"`
+	PublicMCPReusingParams []PublicMCPReusingParam `json:"-"                        gorm:"foreignKey:GroupID"`
+	GroupMCPs              []GroupMCP              `json:"-"                        gorm:"foreignKey:GroupID"`
+	Status                 int                     `json:"status"                   gorm:"default:1;index"`
+	RPMRatio               float64                 `json:"rpm_ratio,omitempty"      gorm:"index"`
+	TPMRatio               float64                 `json:"tpm_ratio,omitempty"      gorm:"index"`
+	UsedAmount             float64                 `json:"used_amount"              gorm:"index"`
+	RequestCount           int                     `json:"request_count"            gorm:"index"`
+	AvailableSets          []string                `json:"available_sets,omitempty" gorm:"serializer:fastjson;type:text"`
 
 	BalanceAlertEnabled   bool    `gorm:"default:false" json:"balance_alert_enabled"`
 	BalanceAlertThreshold float64 `gorm:"default:0"     json:"balance_alert_threshold"`
@@ -44,7 +44,10 @@ func (g *Group) BeforeDelete(tx *gorm.DB) (err error) {
 	if err != nil {
 		return err
 	}
-	err = tx.Model(&PublicMCPReusingParam{}).Where("group_id = ?", g.ID).Delete(&PublicMCPReusingParam{}).Error
+	err = tx.Model(&PublicMCPReusingParam{}).
+		Where("group_id = ?", g.ID).
+		Delete(&PublicMCPReusingParam{}).
+		Error
 	if err != nil {
 		return err
 	}
@@ -52,7 +55,10 @@ func (g *Group) BeforeDelete(tx *gorm.DB) (err error) {
 	if err != nil {
 		return err
 	}
-	return tx.Model(&GroupModelConfig{}).Where("group_id = ?", g.ID).Delete(&GroupModelConfig{}).Error
+	return tx.Model(&GroupModelConfig{}).
+		Where("group_id = ?", g.ID).
+		Delete(&GroupModelConfig{}).
+		Error
 }
 
 func getGroupOrder(order string) string {
@@ -70,7 +76,11 @@ func getGroupOrder(order string) string {
 	}
 }
 
-func GetGroups(page int, perPage int, order string, onlyDisabled bool) (groups []*Group, total int64, err error) {
+func GetGroups(
+	page, perPage int,
+	order string,
+	onlyDisabled bool,
+) (groups []*Group, total int64, err error) {
 	tx := DB.Model(&Group{})
 	if onlyDisabled {
 		tx = tx.Where("status = ?", GroupStatusDisabled)
@@ -255,11 +265,18 @@ func UpdateGroupsStatus(ids []string, status int) (rowsAffected int64, err error
 		}
 	}()
 
-	result := DB.Model(&Group{}).Where("id IN (?) AND status != ?", ids, status).Update("status", status)
+	result := DB.Model(&Group{}).
+		Where("id IN (?) AND status != ?", ids, status).
+		Update("status", status)
 	return result.RowsAffected, result.Error
 }
 
-func SearchGroup(keyword string, page int, perPage int, order string, status int) (groups []*Group, total int64, err error) {
+func SearchGroup(
+	keyword string,
+	page, perPage int,
+	order string,
+	status int,
+) (groups []*Group, total int64, err error) {
 	tx := DB.Model(&Group{})
 	if status != 0 {
 		tx = tx.Where("status = ?", status)

+ 14 - 6
core/model/groupmcp.go

@@ -42,7 +42,7 @@ type GroupMCP struct {
 	Status        GroupMCPStatus       `gorm:"index;default:1"               json:"status"`
 	CreatedAt     time.Time            `gorm:"index,autoCreateTime"          json:"created_at"`
 	UpdateAt      time.Time            `gorm:"index,autoUpdateTime"          json:"update_at"`
-	Name          string               `json:"name"`
+	Name          string               `                                     json:"name"`
 	Type          GroupMCPType         `gorm:"index"                         json:"type"`
 	ProxyConfig   *GroupMCPProxyConfig `gorm:"serializer:fastjson;type:text" json:"proxy_config,omitempty"`
 	OpenAPIConfig *MCPOpenAPIConfig    `gorm:"serializer:fastjson;type:text" json:"openapi_config,omitempty"`
@@ -130,7 +130,7 @@ func UpdateGroupMCP(mcp *GroupMCP) (err error) {
 	return HandleUpdateResult(result, ErrGroupMCPNotFound)
 }
 
-func UpdateGroupMCPStatus(id string, groupID string, status GroupMCPStatus) (err error) {
+func UpdateGroupMCPStatus(id, groupID string, status GroupMCPStatus) (err error) {
 	defer func() {
 		if err == nil {
 			if err := CacheDeleteGroupMCP(groupID, id); err != nil {
@@ -139,12 +139,14 @@ func UpdateGroupMCPStatus(id string, groupID string, status GroupMCPStatus) (err
 		}
 	}()
 
-	result := DB.Model(&GroupMCP{}).Where("id = ? AND group_id = ?", id, groupID).Update("status", status)
+	result := DB.Model(&GroupMCP{}).
+		Where("id = ? AND group_id = ?", id, groupID).
+		Update("status", status)
 	return HandleUpdateResult(result, ErrGroupMCPNotFound)
 }
 
 // DeleteGroupMCP deletes a GroupMCP by ID and GroupID
-func DeleteGroupMCP(id string, groupID string) (err error) {
+func DeleteGroupMCP(id, groupID string) (err error) {
 	defer func() {
 		if err == nil {
 			if err := CacheDeleteGroupMCP(groupID, id); err != nil {
@@ -161,7 +163,7 @@ func DeleteGroupMCP(id string, groupID string) (err error) {
 }
 
 // GetGroupMCPByID retrieves a GroupMCP by ID and GroupID
-func GetGroupMCPByID(id string, groupID string) (*GroupMCP, error) {
+func GetGroupMCPByID(id, groupID string) (*GroupMCP, error) {
 	if id == "" || groupID == "" {
 		return nil, errors.New("group mcp id or group id is empty")
 	}
@@ -171,7 +173,13 @@ func GetGroupMCPByID(id string, groupID string) (*GroupMCP, error) {
 }
 
 // GetGroupMCPs retrieves GroupMCPs with pagination and filtering
-func GetGroupMCPs(groupID string, page int, perPage int, mcpType PublicMCPType, keyword string, status GroupMCPStatus) (mcps []*GroupMCP, total int64, err error) {
+func GetGroupMCPs(
+	groupID string,
+	page, perPage int,
+	mcpType PublicMCPType,
+	keyword string,
+	status GroupMCPStatus,
+) (mcps []*GroupMCP, total int64, err error) {
 	if groupID == "" {
 		return nil, 0, errors.New("group id is empty")
 	}

+ 5 - 3
core/model/groupmodel.go

@@ -21,8 +21,8 @@ type GroupModelConfig struct {
 	TPM           int64 `json:"tpm"`
 
 	OverridePrice bool               `json:"override_price"`
-	ImagePrices   map[string]float64 `gorm:"serializer:fastjson;type:text" json:"image_prices,omitempty"`
-	Price         Price              `gorm:"embedded"                      json:"price,omitempty"`
+	ImagePrices   map[string]float64 `json:"image_prices,omitempty" gorm:"serializer:fastjson;type:text"`
+	Price         Price              `json:"price,omitempty"        gorm:"embedded"`
 
 	OverrideRetryTimes bool  `json:"override_retry_times"`
 	RetryTimes         int64 `json:"retry_times"`
@@ -110,7 +110,9 @@ func DeleteGroupModelConfig(groupID, model string) error {
 }
 
 func DeleteGroupModelConfigs(groupID string, models []string) error {
-	return DB.Where("group_id = ? AND model IN ?", groupID, models).Delete(&GroupModelConfig{}).Error
+	return DB.Where("group_id = ? AND model IN ?", groupID, models).
+		Delete(&GroupModelConfig{}).
+		Error
 }
 
 func GetGroupModelConfigs(groupID string) ([]GroupModelConfig, error) {

+ 7 - 2
core/model/groupsummary.go

@@ -91,7 +91,12 @@ func UpsertGroupSummary(unique GroupSummaryUnique, data SummaryData) error {
 func createGroupSummary(unique GroupSummaryUnique, data SummaryData) error {
 	return LogDB.
 		Clauses(clause.OnConflict{
-			Columns:   []clause.Column{{Name: "group_id"}, {Name: "token_name"}, {Name: "model"}, {Name: "hour_timestamp"}},
+			Columns: []clause.Column{
+				{Name: "group_id"},
+				{Name: "token_name"},
+				{Name: "model"},
+				{Name: "hour_timestamp"},
+			},
 			DoUpdates: clause.Assignments(data.buildUpdateData("group_summaries")),
 		}).
 		Create(&GroupSummary{
@@ -116,7 +121,7 @@ func GetGroupLastRequestTime(group string) (time.Time, error) {
 	return time.Unix(summary.Unique.HourTimestamp, 0), err
 }
 
-func GetGroupTokenLastRequestTime(group string, token string) (time.Time, error) {
+func GetGroupTokenLastRequestTime(group, token string) (time.Time, error) {
 	var summary GroupSummary
 	err := LogDB.
 		Model(&GroupSummary{}).

+ 40 - 25
core/model/log.go

@@ -17,21 +17,23 @@ import (
 )
 
 type RequestDetail struct {
-	CreatedAt             time.Time `gorm:"autoCreateTime;index"              json:"-"`
-	RequestBody           string    `gorm:"type:text"                         json:"request_body,omitempty"`
-	ResponseBody          string    `gorm:"type:text"                         json:"response_body,omitempty"`
-	RequestBodyTruncated  bool      `json:"request_body_truncated,omitempty"`
-	ResponseBodyTruncated bool      `json:"response_body_truncated,omitempty"`
-	ID                    int       `gorm:"primaryKey"                        json:"id"`
-	LogID                 int       `gorm:"index"                             json:"log_id"`
+	CreatedAt             time.Time `gorm:"autoCreateTime;index" json:"-"`
+	RequestBody           string    `gorm:"type:text"            json:"request_body,omitempty"`
+	ResponseBody          string    `gorm:"type:text"            json:"response_body,omitempty"`
+	RequestBodyTruncated  bool      `                            json:"request_body_truncated,omitempty"`
+	ResponseBodyTruncated bool      `                            json:"response_body_truncated,omitempty"`
+	ID                    int       `gorm:"primaryKey"           json:"id"`
+	LogID                 int       `gorm:"index"                json:"log_id"`
 }
 
 func (d *RequestDetail) BeforeSave(_ *gorm.DB) (err error) {
-	if reqMax := config.GetLogDetailRequestBodyMaxSize(); reqMax > 0 && int64(len(d.RequestBody)) > reqMax {
+	if reqMax := config.GetLogDetailRequestBodyMaxSize(); reqMax > 0 &&
+		int64(len(d.RequestBody)) > reqMax {
 		d.RequestBody = common.TruncateByRune(d.RequestBody, int(reqMax)) + "..."
 		d.RequestBodyTruncated = true
 	}
-	if respMax := config.GetLogDetailResponseBodyMaxSize(); respMax > 0 && int64(len(d.ResponseBody)) > respMax {
+	if respMax := config.GetLogDetailResponseBodyMaxSize(); respMax > 0 &&
+		int64(len(d.ResponseBody)) > respMax {
 		d.ResponseBody = common.TruncateByRune(d.ResponseBody, int(respMax)) + "..."
 		d.ResponseBodyTruncated = true
 	}
@@ -50,7 +52,8 @@ type Price struct {
 	OutputPrice     ZeroNullFloat64 `json:"output_price,omitempty"`
 	OutputPriceUnit ZeroNullInt64   `json:"output_price_unit,omitempty"`
 
-	// when ThinkingModeOutputPrice and ReasoningTokens are not 0, OutputPrice and OutputPriceUnit will be overwritten
+	// when ThinkingModeOutputPrice and ReasoningTokens are not 0, OutputPrice and OutputPriceUnit
+	// will be overwritten
 	ThinkingModeOutputPrice     ZeroNullFloat64 `json:"thinking_mode_output_price,omitempty"`
 	ThinkingModeOutputPriceUnit ZeroNullInt64   `json:"thinking_mode_output_price_unit,omitempty"`
 
@@ -129,29 +132,29 @@ func (u *Usage) Add(other Usage) {
 
 type Log struct {
 	RequestDetail    *RequestDetail  `gorm:"foreignKey:LogID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"request_detail,omitempty"`
-	RequestAt        time.Time       `json:"request_at"`
-	RetryAt          time.Time       `json:"retry_at,omitempty"`
-	TTFBMilliseconds ZeroNullInt64   `json:"ttfb_milliseconds,omitempty"`
+	RequestAt        time.Time       `                                                                      json:"request_at"`
+	RetryAt          time.Time       `                                                                      json:"retry_at,omitempty"`
+	TTFBMilliseconds ZeroNullInt64   `                                                                      json:"ttfb_milliseconds,omitempty"`
 	CreatedAt        time.Time       `gorm:"autoCreateTime;index"                                           json:"created_at"`
-	TokenName        string          `json:"token_name,omitempty"`
-	Endpoint         EmptyNullString `json:"endpoint,omitempty"`
+	TokenName        string          `                                                                      json:"token_name,omitempty"`
+	Endpoint         EmptyNullString `                                                                      json:"endpoint,omitempty"`
 	Content          EmptyNullString `gorm:"type:text"                                                      json:"content,omitempty"`
-	GroupID          string          `json:"group,omitempty"`
-	Model            string          `json:"model"`
+	GroupID          string          `                                                                      json:"group,omitempty"`
+	Model            string          `                                                                      json:"model"`
 	RequestID        EmptyNullString `gorm:"index:,where:request_id is not null"                            json:"request_id"`
 	ID               int             `gorm:"primaryKey"                                                     json:"id"`
 	TokenID          int             `gorm:"index"                                                          json:"token_id,omitempty"`
-	ChannelID        int             `json:"channel,omitempty"`
+	ChannelID        int             `                                                                      json:"channel,omitempty"`
 	Code             int             `gorm:"index"                                                          json:"code,omitempty"`
-	Mode             int             `json:"mode,omitempty"`
+	Mode             int             `                                                                      json:"mode,omitempty"`
 	IP               EmptyNullString `gorm:"index:,where:ip is not null"                                    json:"ip,omitempty"`
-	RetryTimes       ZeroNullInt64   `json:"retry_times,omitempty"`
+	RetryTimes       ZeroNullInt64   `                                                                      json:"retry_times,omitempty"`
 	Price            Price           `gorm:"embedded"                                                       json:"price,omitempty"`
 	Usage            Usage           `gorm:"embedded"                                                       json:"usage,omitempty"`
-	UsedAmount       float64         `json:"used_amount,omitempty"`
+	UsedAmount       float64         `                                                                      json:"used_amount,omitempty"`
 	// https://platform.openai.com/docs/guides/safety-best-practices#end-user-ids
-	User     EmptyNullString   `json:"user,omitempty"`
-	Metadata map[string]string `gorm:"serializer:fastjson;type:text" json:"metadata,omitempty"`
+	User     EmptyNullString   `                                                                      json:"user,omitempty"`
+	Metadata map[string]string `gorm:"serializer:fastjson;type:text"                                  json:"metadata,omitempty"`
 }
 
 func CreateLogIndexes(db *gorm.DB) error {
@@ -1252,7 +1255,10 @@ func GetUsedTokenNamesFromLog(group string, start, end time.Time) ([]string, err
 	return getLogGroupByValuesFromLog[string]("token_name", group, start, end)
 }
 
-func getLogGroupByValuesFromLog[T cmp.Ordered](field string, group string, start, end time.Time) ([]T, error) {
+func getLogGroupByValuesFromLog[T cmp.Ordered](
+	field, group string,
+	start, end time.Time,
+) ([]T, error) {
 	var values []T
 	query := LogDB.
 		Model(&Log{})
@@ -1396,7 +1402,16 @@ func GetGroupDashboardData(
 
 	g.Go(func() error {
 		var err error
-		chartData, err = getChartData(group, start, end, tokenName, modelName, 0, timeSpan, timezone)
+		chartData, err = getChartData(
+			group,
+			start,
+			end,
+			tokenName,
+			modelName,
+			0,
+			timeSpan,
+			timezone,
+		)
 		return err
 	})
 

+ 19 - 5
core/model/main.go

@@ -11,10 +11,9 @@ import (
 	"github.com/labring/aiproxy/core/common"
 	"github.com/labring/aiproxy/core/common/config"
 	"github.com/labring/aiproxy/core/common/env"
-	"github.com/labring/aiproxy/core/common/notify"
-
 	// import fastjson serializer
 	_ "github.com/labring/aiproxy/core/common/fastJSONSerializer"
+	"github.com/labring/aiproxy/core/common/notify"
 	log "github.com/sirupsen/logrus"
 	"gorm.io/driver/mysql"
 	"gorm.io/driver/postgres"
@@ -206,15 +205,30 @@ func migrateLOGDB() error {
 	go func() {
 		err := CreateLogIndexes(LogDB)
 		if err != nil {
-			notify.ErrorThrottle("createLogIndexes", time.Minute, "failed to create log indexes", err.Error())
+			notify.ErrorThrottle(
+				"createLogIndexes",
+				time.Minute,
+				"failed to create log indexes",
+				err.Error(),
+			)
 		}
 		err = CreateSummaryIndexs(LogDB)
 		if err != nil {
-			notify.ErrorThrottle("createSummaryIndexs", time.Minute, "failed to create summary indexs", err.Error())
+			notify.ErrorThrottle(
+				"createSummaryIndexs",
+				time.Minute,
+				"failed to create summary indexs",
+				err.Error(),
+			)
 		}
 		err = CreateGroupSummaryIndexs(LogDB)
 		if err != nil {
-			notify.ErrorThrottle("createGroupSummaryIndexs", time.Minute, "failed to create group summary indexs", err.Error())
+			notify.ErrorThrottle(
+				"createGroupSummaryIndexs",
+				time.Minute,
+				"failed to create group summary indexs",
+				err.Error(),
+			)
 		}
 	}()
 	return nil

+ 17 - 9
core/model/modelconfig.go

@@ -26,18 +26,18 @@ type ModelConfig struct {
 	Plugin           map[string]json.RawMessage `gorm:"serializer:fastjson;type:text" json:"plugin,omitempty"`
 	Model            string                     `gorm:"primaryKey"                    json:"model"`
 	Owner            ModelOwner                 `gorm:"type:varchar(255);index"       json:"owner"`
-	Type             mode.Mode                  `json:"type"`
-	ExcludeFromTests bool                       `json:"exclude_from_tests,omitempty"`
-	RPM              int64                      `json:"rpm,omitempty"`
-	TPM              int64                      `json:"tpm,omitempty"`
+	Type             mode.Mode                  `                                     json:"type"`
+	ExcludeFromTests bool                       `                                     json:"exclude_from_tests,omitempty"`
+	RPM              int64                      `                                     json:"rpm,omitempty"`
+	TPM              int64                      `                                     json:"tpm,omitempty"`
 	// map[size]map[quality]price_per_image
 	ImageQualityPrices map[string]map[string]float64 `gorm:"serializer:fastjson;type:text" json:"image_quality_prices,omitempty"`
 	// map[size]price_per_image
 	ImagePrices  map[string]float64 `gorm:"serializer:fastjson;type:text" json:"image_prices,omitempty"`
 	Price        Price              `gorm:"embedded"                      json:"price,omitempty"`
-	RetryTimes   int64              `json:"retry_times,omitempty"`
-	Timeout      int64              `json:"timeout,omitempty"`
-	MaxErrorRate float64            `json:"max_error_rate,omitempty"`
+	RetryTimes   int64              `                                     json:"retry_times,omitempty"`
+	Timeout      int64              `                                     json:"timeout,omitempty"`
+	MaxErrorRate float64            `                                     json:"max_error_rate,omitempty"`
 }
 
 func (c *ModelConfig) BeforeSave(_ *gorm.DB) (err error) {
@@ -126,7 +126,10 @@ func (c *ModelConfig) SupportFormats() ([]string, bool) {
 	return GetModelConfigStringSlice(c.Config, ModelConfigSupportFormatsKey)
 }
 
-func GetModelConfigs(page int, perPage int, model string) (configs []*ModelConfig, total int64, err error) {
+func GetModelConfigs(
+	page, perPage int,
+	model string,
+) (configs []*ModelConfig, total int64, err error) {
 	tx := DB.Model(&ModelConfig{})
 	if model != "" {
 		tx = tx.Where("model = ?", model)
@@ -177,7 +180,12 @@ func GetModelConfig(model string) (ModelConfig, error) {
 	return config, HandleNotFound(err, ErrModelConfigNotFound)
 }
 
-func SearchModelConfigs(keyword string, page int, perPage int, model string, owner ModelOwner) (configs []ModelConfig, total int64, err error) {
+func SearchModelConfigs(
+	keyword string,
+	page, perPage int,
+	model string,
+	owner ModelOwner,
+) (configs []ModelConfig, total int64, err error) {
 	tx := DB.Model(&ModelConfig{}).Where("model LIKE ?", "%"+keyword+"%")
 	if model != "" {
 		tx = tx.Where("model = ?", model)

+ 24 - 8
core/model/option.go

@@ -19,7 +19,7 @@ import (
 
 type Option struct {
 	Key   string `gorm:"primaryKey" json:"key"`
-	Value string `json:"value"`
+	Value string `                  json:"value"`
 }
 
 func GetAllOption() ([]*Option, error) {
@@ -64,8 +64,14 @@ func initOptionMap() error {
 	optionMap["IPGroupsThreshold"] = strconv.FormatInt(config.GetIPGroupsThreshold(), 10)
 	optionMap["IPGroupsBanThreshold"] = strconv.FormatInt(config.GetIPGroupsBanThreshold(), 10)
 	optionMap["SaveAllLogDetail"] = strconv.FormatBool(config.GetSaveAllLogDetail())
-	optionMap["LogDetailRequestBodyMaxSize"] = strconv.FormatInt(config.GetLogDetailRequestBodyMaxSize(), 10)
-	optionMap["LogDetailResponseBodyMaxSize"] = strconv.FormatInt(config.GetLogDetailResponseBodyMaxSize(), 10)
+	optionMap["LogDetailRequestBodyMaxSize"] = strconv.FormatInt(
+		config.GetLogDetailRequestBodyMaxSize(),
+		10,
+	)
+	optionMap["LogDetailResponseBodyMaxSize"] = strconv.FormatInt(
+		config.GetLogDetailResponseBodyMaxSize(),
+		10,
+	)
 	optionMap["DisableServe"] = strconv.FormatBool(config.GetDisableServe())
 	optionMap["BillingEnabled"] = strconv.FormatBool(config.GetBillingEnabled())
 	optionMap["RetryTimes"] = strconv.FormatInt(config.GetRetryTimes(), 10)
@@ -113,7 +119,12 @@ func loadOptionsFromDatabase(isInit bool) error {
 		err := updateOption(option.Key, option.Value, isInit)
 		if err != nil {
 			if !errors.Is(err, ErrUnknownOptionKey) {
-				return fmt.Errorf("failed to update option: %s, value: %s, error: %w", option.Key, option.Value, err)
+				return fmt.Errorf(
+					"failed to update option: %s, value: %s, error: %w",
+					option.Key,
+					option.Value,
+					err,
+				)
 			}
 			if isInit {
 				log.Warnf("unknown option: %s, value: %s", option.Key, option.Value)
@@ -138,13 +149,18 @@ func SyncOptions(ctx context.Context, wg *sync.WaitGroup, frequency time.Duratio
 			return
 		case <-ticker.C:
 			if err := loadOptionsFromDatabase(false); err != nil {
-				notify.ErrorThrottle("syncOptions", time.Minute, "failed to sync options", err.Error())
+				notify.ErrorThrottle(
+					"syncOptions",
+					time.Minute,
+					"failed to sync options",
+					err.Error(),
+				)
 			}
 		}
 	}
 }
 
-func saveOption(key string, value string) error {
+func saveOption(key, value string) error {
 	option := Option{
 		Key:   key,
 		Value: value,
@@ -153,7 +169,7 @@ func saveOption(key string, value string) error {
 	return HandleUpdateResult(result, "option:"+key)
 }
 
-func UpdateOption(key string, value string) error {
+func UpdateOption(key, value string) error {
 	err := updateOption(key, value, false)
 	if err != nil {
 		return err
@@ -183,7 +199,7 @@ func toBool(value string) bool {
 }
 
 //nolint:gocyclo
-func updateOption(key string, value string, isInit bool) (err error) {
+func updateOption(key, value string, isInit bool) (err error) {
 	switch key {
 	case "LogStorageHours":
 		logStorageHours, err := strconv.ParseInt(value, 10, 64)

+ 24 - 10
core/model/publicmcp.go

@@ -49,7 +49,7 @@ type ReusingParam struct {
 
 type MCPPrice struct {
 	DefaultToolsCallPrice float64            `json:"default_tools_call_price"`
-	ToolsCallPrices       map[string]float64 `gorm:"serializer:fastjson;type:text" json:"tools_call_prices"`
+	ToolsCallPrices       map[string]float64 `json:"tools_call_prices"        gorm:"serializer:fastjson;type:text"`
 }
 
 type PublicMCPProxyConfig struct {
@@ -117,13 +117,13 @@ type PublicMCP struct {
 	CreatedAt              time.Time               `gorm:"index,autoCreateTime"          json:"created_at"`
 	UpdateAt               time.Time               `gorm:"index,autoUpdateTime"          json:"update_at"`
 	PublicMCPReusingParams []PublicMCPReusingParam `gorm:"foreignKey:MCPID"              json:"-"`
-	Name                   string                  `json:"name"`
+	Name                   string                  `                                     json:"name"`
 	Type                   PublicMCPType           `gorm:"index"                         json:"type"`
-	RepoURL                string                  `json:"repo_url"`
-	ReadmeURL              string                  `json:"readme_url"`
+	RepoURL                string                  `                                     json:"repo_url"`
+	ReadmeURL              string                  `                                     json:"readme_url"`
 	Readme                 string                  `gorm:"type:text"                     json:"readme"`
 	Tags                   []string                `gorm:"serializer:fastjson;type:text" json:"tags,omitempty"`
-	LogoURL                string                  `json:"logo_url"`
+	LogoURL                string                  `                                     json:"logo_url"`
 	Price                  MCPPrice                `gorm:"embedded"                      json:"price"`
 	ProxyConfig            *PublicMCPProxyConfig   `gorm:"serializer:fastjson;type:text" json:"proxy_config,omitempty"`
 	OpenAPIConfig          *MCPOpenAPIConfig       `gorm:"serializer:fastjson;type:text" json:"openapi_config,omitempty"`
@@ -172,7 +172,10 @@ func validateHTTPURL(str string) error {
 }
 
 func (p *PublicMCP) BeforeDelete(tx *gorm.DB) (err error) {
-	return tx.Model(&PublicMCPReusingParam{}).Where("mcp_id = ?", p.ID).Delete(&PublicMCPReusingParam{}).Error
+	return tx.Model(&PublicMCPReusingParam{}).
+		Where("mcp_id = ?", p.ID).
+		Delete(&PublicMCPReusingParam{}).
+		Error
 }
 
 func (p *PublicMCP) MarshalJSON() ([]byte, error) {
@@ -292,7 +295,12 @@ func GetPublicMCPByID(id string) (*PublicMCP, error) {
 }
 
 // GetPublicMCPs retrieves MCPs with pagination and filtering
-func GetPublicMCPs(page int, perPage int, mcpType PublicMCPType, keyword string, status PublicMCPStatus) (mcps []*PublicMCP, total int64, err error) {
+func GetPublicMCPs(
+	page, perPage int,
+	mcpType PublicMCPType,
+	keyword string,
+	status PublicMCPStatus,
+) (mcps []*PublicMCP, total int64, err error) {
 	tx := DB.Model(&PublicMCP{})
 
 	if mcpType != "" {
@@ -302,7 +310,13 @@ func GetPublicMCPs(page int, perPage int, mcpType PublicMCPType, keyword string,
 	if keyword != "" {
 		keyword = "%" + keyword + "%"
 		if common.UsingPostgreSQL {
-			tx = tx.Where("name ILIKE ? OR author ILIKE ? OR tags ILIKE ? OR id ILIKE ?", keyword, keyword, keyword, keyword)
+			tx = tx.Where(
+				"name ILIKE ? OR author ILIKE ? OR tags ILIKE ? OR id ILIKE ?",
+				keyword,
+				keyword,
+				keyword,
+				keyword,
+			)
 		} else {
 			tx = tx.Where("name LIKE ? OR author LIKE ? OR tags LIKE ? OR id LIKE ?", keyword, keyword, keyword, keyword)
 		}
@@ -376,7 +390,7 @@ func UpdatePublicMCPReusingParam(param *PublicMCPReusingParam) (err error) {
 }
 
 // DeletePublicMCPReusingParam deletes a GroupMCPReusingParam
-func DeletePublicMCPReusingParam(mcpID string, groupID string) (err error) {
+func DeletePublicMCPReusingParam(mcpID, groupID string) (err error) {
 	defer func() {
 		if err == nil {
 			if err := CacheDeletePublicMCPReusingParam(mcpID, groupID); err != nil {
@@ -395,7 +409,7 @@ func DeletePublicMCPReusingParam(mcpID string, groupID string) (err error) {
 }
 
 // GetPublicMCPReusingParam retrieves a GroupMCPReusingParam by MCP ID and Group ID
-func GetPublicMCPReusingParam(mcpID string, groupID string) (*PublicMCPReusingParam, error) {
+func GetPublicMCPReusingParam(mcpID, groupID string) (*PublicMCPReusingParam, error) {
 	if mcpID == "" || groupID == "" {
 		return nil, errors.New("MCP ID or Group ID is empty")
 	}

+ 13 - 11
core/model/retrylog.go

@@ -12,27 +12,29 @@ import (
 type RetryLog struct {
 	RequestBody           string          `gorm:"type:text"                           json:"request_body,omitempty"`
 	ResponseBody          string          `gorm:"type:text"                           json:"response_body,omitempty"`
-	RequestBodyTruncated  bool            `json:"request_body_truncated,omitempty"`
-	ResponseBodyTruncated bool            `json:"response_body_truncated,omitempty"`
-	RequestAt             time.Time       `json:"request_at"`
-	RetryAt               time.Time       `json:"retry_at,omitempty"`
-	TTFBMilliseconds      ZeroNullInt64   `json:"ttfb_milliseconds,omitempty"`
+	RequestBodyTruncated  bool            `                                           json:"request_body_truncated,omitempty"`
+	ResponseBodyTruncated bool            `                                           json:"response_body_truncated,omitempty"`
+	RequestAt             time.Time       `                                           json:"request_at"`
+	RetryAt               time.Time       `                                           json:"retry_at,omitempty"`
+	TTFBMilliseconds      ZeroNullInt64   `                                           json:"ttfb_milliseconds,omitempty"`
 	CreatedAt             time.Time       `gorm:"autoCreateTime;index"                json:"created_at"`
-	Model                 string          `json:"model"`
+	Model                 string          `                                           json:"model"`
 	RequestID             EmptyNullString `gorm:"index:,where:request_id is not null" json:"request_id"`
 	ID                    int             `gorm:"primaryKey"                          json:"id"`
-	ChannelID             int             `json:"channel,omitempty"`
+	ChannelID             int             `                                           json:"channel,omitempty"`
 	Code                  int             `gorm:"index"                               json:"code,omitempty"`
-	Mode                  int             `json:"mode,omitempty"`
-	RetryTimes            ZeroNullInt64   `json:"retry_times,omitempty"`
+	Mode                  int             `                                           json:"mode,omitempty"`
+	RetryTimes            ZeroNullInt64   `                                           json:"retry_times,omitempty"`
 }
 
 func (r *RetryLog) BeforeSave(_ *gorm.DB) (err error) {
-	if reqMax := config.GetLogDetailRequestBodyMaxSize(); reqMax > 0 && int64(len(r.RequestBody)) > reqMax {
+	if reqMax := config.GetLogDetailRequestBodyMaxSize(); reqMax > 0 &&
+		int64(len(r.RequestBody)) > reqMax {
 		r.RequestBody = common.TruncateByRune(r.RequestBody, int(reqMax)) + "..."
 		r.RequestBodyTruncated = true
 	}
-	if respMax := config.GetLogDetailResponseBodyMaxSize(); respMax > 0 && int64(len(r.ResponseBody)) > respMax {
+	if respMax := config.GetLogDetailResponseBodyMaxSize(); respMax > 0 &&
+		int64(len(r.ResponseBody)) > respMax {
 		r.ResponseBody = common.TruncateByRune(r.ResponseBody, int(respMax)) + "..."
 		r.ResponseBodyTruncated = true
 	}

+ 83 - 17
core/model/summary.go

@@ -32,7 +32,7 @@ type SummaryData struct {
 	MaxRPS         int64   `json:"max_rps,omitempty"`
 	MaxTPM         int64   `json:"max_tpm,omitempty"`
 	MaxTPS         int64   `json:"max_tps,omitempty"`
-	Usage          Usage   `gorm:"embedded"          json:"usage,omitempty"`
+	Usage          Usage   `json:"usage,omitempty"   gorm:"embedded"`
 }
 
 func (d *SummaryData) buildUpdateData(tableName string) map[string]any {
@@ -49,39 +49,92 @@ func (d *SummaryData) buildUpdateData(tableName string) map[string]any {
 
 	// max rpm tpm update
 	if d.MaxRPM > 0 {
-		data["max_rpm"] = gorm.Expr(fmt.Sprintf("CASE WHEN %s.max_rpm < ? THEN ? ELSE %s.max_rpm END", tableName, tableName), d.MaxRPM, d.MaxRPM)
+		data["max_rpm"] = gorm.Expr(
+			fmt.Sprintf(
+				"CASE WHEN %s.max_rpm < ? THEN ? ELSE %s.max_rpm END",
+				tableName,
+				tableName,
+			),
+			d.MaxRPM,
+			d.MaxRPM,
+		)
 	}
 	if d.MaxRPS > 0 {
-		data["max_rps"] = gorm.Expr(fmt.Sprintf("CASE WHEN %s.max_rps < ? THEN ? ELSE %s.max_rps END", tableName, tableName), d.MaxRPS, d.MaxRPS)
+		data["max_rps"] = gorm.Expr(
+			fmt.Sprintf(
+				"CASE WHEN %s.max_rps < ? THEN ? ELSE %s.max_rps END",
+				tableName,
+				tableName,
+			),
+			d.MaxRPS,
+			d.MaxRPS,
+		)
 	}
 	if d.MaxTPM > 0 {
-		data["max_tpm"] = gorm.Expr(fmt.Sprintf("CASE WHEN %s.max_tpm < ? THEN ? ELSE %s.max_tpm END", tableName, tableName), d.MaxTPM, d.MaxTPM)
+		data["max_tpm"] = gorm.Expr(
+			fmt.Sprintf(
+				"CASE WHEN %s.max_tpm < ? THEN ? ELSE %s.max_tpm END",
+				tableName,
+				tableName,
+			),
+			d.MaxTPM,
+			d.MaxTPM,
+		)
 	}
 	if d.MaxTPS > 0 {
-		data["max_tps"] = gorm.Expr(fmt.Sprintf("CASE WHEN %s.max_tps < ? THEN ? ELSE %s.max_tps END", tableName, tableName), d.MaxTPS, d.MaxTPS)
+		data["max_tps"] = gorm.Expr(
+			fmt.Sprintf(
+				"CASE WHEN %s.max_tps < ? THEN ? ELSE %s.max_tps END",
+				tableName,
+				tableName,
+			),
+			d.MaxTPS,
+			d.MaxTPS,
+		)
 	}
 
 	// usage update
 	if d.Usage.InputTokens > 0 {
-		data["input_tokens"] = gorm.Expr(fmt.Sprintf("COALESCE(%s.input_tokens, 0) + ?", tableName), d.Usage.InputTokens)
+		data["input_tokens"] = gorm.Expr(
+			fmt.Sprintf("COALESCE(%s.input_tokens, 0) + ?", tableName),
+			d.Usage.InputTokens,
+		)
 	}
 	if d.Usage.ImageInputTokens > 0 {
-		data["image_input_tokens"] = gorm.Expr(fmt.Sprintf("COALESCE(%s.image_input_tokens, 0) + ?", tableName), d.Usage.ImageInputTokens)
+		data["image_input_tokens"] = gorm.Expr(
+			fmt.Sprintf("COALESCE(%s.image_input_tokens, 0) + ?", tableName),
+			d.Usage.ImageInputTokens,
+		)
 	}
 	if d.Usage.OutputTokens > 0 {
-		data["output_tokens"] = gorm.Expr(fmt.Sprintf("COALESCE(%s.output_tokens, 0) + ?", tableName), d.Usage.OutputTokens)
+		data["output_tokens"] = gorm.Expr(
+			fmt.Sprintf("COALESCE(%s.output_tokens, 0) + ?", tableName),
+			d.Usage.OutputTokens,
+		)
 	}
 	if d.Usage.TotalTokens > 0 {
-		data["total_tokens"] = gorm.Expr(fmt.Sprintf("COALESCE(%s.total_tokens, 0) + ?", tableName), d.Usage.TotalTokens)
+		data["total_tokens"] = gorm.Expr(
+			fmt.Sprintf("COALESCE(%s.total_tokens, 0) + ?", tableName),
+			d.Usage.TotalTokens,
+		)
 	}
 	if d.Usage.CachedTokens > 0 {
-		data["cached_tokens"] = gorm.Expr(fmt.Sprintf("COALESCE(%s.cached_tokens, 0) + ?", tableName), d.Usage.CachedTokens)
+		data["cached_tokens"] = gorm.Expr(
+			fmt.Sprintf("COALESCE(%s.cached_tokens, 0) + ?", tableName),
+			d.Usage.CachedTokens,
+		)
 	}
 	if d.Usage.CacheCreationTokens > 0 {
-		data["cache_creation_tokens"] = gorm.Expr(fmt.Sprintf("COALESCE(%s.cache_creation_tokens, 0) + ?", tableName), d.Usage.CacheCreationTokens)
+		data["cache_creation_tokens"] = gorm.Expr(
+			fmt.Sprintf("COALESCE(%s.cache_creation_tokens, 0) + ?", tableName),
+			d.Usage.CacheCreationTokens,
+		)
 	}
 	if d.Usage.WebSearchCount > 0 {
-		data["web_search_count"] = gorm.Expr(fmt.Sprintf("COALESCE(%s.web_search_count, 0) + ?", tableName), d.Usage.WebSearchCount)
+		data["web_search_count"] = gorm.Expr(
+			fmt.Sprintf("COALESCE(%s.web_search_count, 0) + ?", tableName),
+			d.Usage.WebSearchCount,
+		)
 	}
 	return data
 }
@@ -165,7 +218,11 @@ func UpsertSummary(unique SummaryUnique, data SummaryData) error {
 func createSummary(unique SummaryUnique, data SummaryData) error {
 	return LogDB.
 		Clauses(clause.OnConflict{
-			Columns:   []clause.Column{{Name: "channel_id"}, {Name: "model"}, {Name: "hour_timestamp"}},
+			Columns: []clause.Column{
+				{Name: "channel_id"},
+				{Name: "model"},
+				{Name: "hour_timestamp"},
+			},
 			DoUpdates: clause.Assignments(data.buildUpdateData("summaries")),
 		}).
 		Create(&Summary{
@@ -250,7 +307,7 @@ func GetUsedChannels(group string, start, end time.Time) ([]int, error) {
 	return getLogGroupByValues[int]("channel_id", group, "", start, end)
 }
 
-func GetUsedModels(group string, tokenName string, start, end time.Time) ([]string, error) {
+func GetUsedModels(group, tokenName string, start, end time.Time) ([]string, error) {
 	return getLogGroupByValues[string]("model", group, tokenName, start, end)
 }
 
@@ -258,7 +315,10 @@ func GetUsedTokenNames(group string, start, end time.Time) ([]string, error) {
 	return getLogGroupByValues[string]("token_name", group, "", start, end)
 }
 
-func getLogGroupByValues[T cmp.Ordered](field string, group string, tokenName string, start, end time.Time) ([]T, error) {
+func getLogGroupByValues[T cmp.Ordered](
+	field, group, tokenName string,
+	start, end time.Time,
+) ([]T, error) {
 	type Result struct {
 		Value        T
 		UsedAmount   float64
@@ -290,7 +350,9 @@ func getLogGroupByValues[T cmp.Ordered](field string, group string, tokenName st
 	}
 
 	err := query.
-		Select(field + " as value, SUM(request_count) as request_count, SUM(used_amount) as used_amount").
+		Select(
+			field + " as value, SUM(request_count) as request_count, SUM(used_amount) as used_amount",
+		).
 		Group(field).
 		Scan(&results).Error
 	if err != nil {
@@ -332,7 +394,11 @@ type CostRank struct {
 	MaxTPS int64 `json:"max_tps,omitempty"`
 }
 
-func GetModelCostRank(group string, tokenName string, channelID int, start, end time.Time) ([]*CostRank, error) {
+func GetModelCostRank(
+	group, tokenName string,
+	channelID int,
+	start, end time.Time,
+) ([]*CostRank, error) {
 	var ranks []*CostRank
 
 	var query *gorm.DB

+ 33 - 16
core/model/token.go

@@ -27,17 +27,17 @@ const (
 type Token struct {
 	CreatedAt    time.Time       `json:"created_at"`
 	ExpiredAt    time.Time       `json:"expired_at"`
-	Group        *Group          `gorm:"foreignKey:GroupID"                        json:"-"`
-	Key          string          `gorm:"type:char(48);uniqueIndex"                 json:"key"`
-	Name         EmptyNullString `gorm:"index;uniqueIndex:idx_group_name;not null" json:"name"`
-	GroupID      string          `gorm:"index;uniqueIndex:idx_group_name"          json:"group"`
-	Subnets      []string        `gorm:"serializer:fastjson;type:text"             json:"subnets"`
-	Models       []string        `gorm:"serializer:fastjson;type:text"             json:"models"`
-	Status       int             `gorm:"default:1;index"                           json:"status"`
-	ID           int             `gorm:"primaryKey"                                json:"id"`
+	Group        *Group          `json:"-"             gorm:"foreignKey:GroupID"`
+	Key          string          `json:"key"           gorm:"type:char(48);uniqueIndex"`
+	Name         EmptyNullString `json:"name"          gorm:"index;uniqueIndex:idx_group_name;not null"`
+	GroupID      string          `json:"group"         gorm:"index;uniqueIndex:idx_group_name"`
+	Subnets      []string        `json:"subnets"       gorm:"serializer:fastjson;type:text"`
+	Models       []string        `json:"models"        gorm:"serializer:fastjson;type:text"`
+	Status       int             `json:"status"        gorm:"default:1;index"`
+	ID           int             `json:"id"            gorm:"primaryKey"`
 	Quota        float64         `json:"quota"`
-	UsedAmount   float64         `gorm:"index"                                     json:"used_amount"`
-	RequestCount int             `gorm:"index"                                     json:"request_count"`
+	UsedAmount   float64         `json:"used_amount"   gorm:"index"`
+	RequestCount int             `json:"request_count" gorm:"index"`
 }
 
 func (t *Token) BeforeCreate(_ *gorm.DB) (err error) {
@@ -75,7 +75,7 @@ func getTokenOrder(order string) string {
 	}
 }
 
-func InsertToken(token *Token, autoCreateGroup bool, ignoreExist bool) error {
+func InsertToken(token *Token, autoCreateGroup, ignoreExist bool) error {
 	if autoCreateGroup {
 		group := &Group{
 			ID: token.GroupID,
@@ -115,7 +115,12 @@ func InsertToken(token *Token, autoCreateGroup bool, ignoreExist bool) error {
 	return nil
 }
 
-func GetTokens(group string, page int, perPage int, order string, status int) (tokens []*Token, total int64, err error) {
+func GetTokens(
+	group string,
+	page, perPage int,
+	order string,
+	status int,
+) (tokens []*Token, total int64, err error) {
 	tx := DB.Model(&Token{})
 	if group != "" {
 		tx = tx.Where("group_id = ?", group)
@@ -138,7 +143,13 @@ func GetTokens(group string, page int, perPage int, order string, status int) (t
 	return tokens, total, err
 }
 
-func SearchTokens(group string, keyword string, page int, perPage int, order string, status int, name string, key string) (tokens []*Token, total int64, err error) {
+func SearchTokens(
+	group, keyword string,
+	page, perPage int,
+	order string,
+	status int,
+	name, key string,
+) (tokens []*Token, total int64, err error) {
 	tx := DB.Model(&Token{})
 	if group != "" {
 		tx = tx.Where("group_id = ?", group)
@@ -211,7 +222,13 @@ func SearchTokens(group string, keyword string, page int, perPage int, order str
 	return tokens, total, err
 }
 
-func SearchGroupTokens(group string, keyword string, page int, perPage int, order string, status int, name string, key string) (tokens []*Token, total int64, err error) {
+func SearchGroupTokens(
+	group, keyword string,
+	page, perPage int,
+	order string,
+	status int,
+	name, key string,
+) (tokens []*Token, total int64, err error) {
 	if group == "" {
 		return nil, 0, errors.New("group is empty")
 	}
@@ -331,7 +348,7 @@ func GetTokenByID(id int) (*Token, error) {
 	return &token, HandleNotFound(err, ErrTokenNotFound)
 }
 
-func UpdateTokenStatus(id int, status int) (err error) {
+func UpdateTokenStatus(id, status int) (err error) {
 	token := Token{ID: id}
 	defer func() {
 		if err == nil {
@@ -356,7 +373,7 @@ func UpdateTokenStatus(id int, status int) (err error) {
 	return HandleUpdateResult(result, ErrTokenNotFound)
 }
 
-func UpdateGroupTokenStatus(group string, id int, status int) (err error) {
+func UpdateGroupTokenStatus(group string, id, status int) (err error) {
 	if id == 0 || group == "" {
 		return errors.New("id or group is empty")
 	}

+ 1 - 1
core/model/utils.go

@@ -148,7 +148,7 @@ func String2Int(keyword string) int {
 	return i
 }
 
-func toLimitOffset(page int, perPage int) (limit int, offset int) {
+func toLimitOffset(page, perPage int) (limit, offset int) {
 	page--
 	if page < 0 {
 		page = 0

+ 32 - 7
core/monitor/memmodel.go

@@ -92,7 +92,12 @@ func (m *MemModelMonitor) cleanupExpiredData() {
 	}
 }
 
-func (m *MemModelMonitor) AddRequest(model string, channelID int64, isError, tryBan bool, maxErrorRate float64) (beyondThreshold, banExecution bool) {
+func (m *MemModelMonitor) AddRequest(
+	model string,
+	channelID int64,
+	isError, tryBan bool,
+	maxErrorRate float64,
+) (beyondThreshold, banExecution bool) {
 	m.mu.Lock()
 	defer m.mu.Unlock()
 
@@ -122,7 +127,12 @@ func (m *MemModelMonitor) AddRequest(model string, channelID int64, isError, try
 	return m.checkAndBan(now, channel, tryBan, maxErrorRate)
 }
 
-func (m *MemModelMonitor) checkAndBan(now time.Time, channel *ChannelStats, tryBan bool, maxErrorRate float64) (beyondThreshold, banExecution bool) {
+func (m *MemModelMonitor) checkAndBan(
+	now time.Time,
+	channel *ChannelStats,
+	tryBan bool,
+	maxErrorRate float64,
+) (beyondThreshold, banExecution bool) {
 	canBan := maxErrorRate > 0
 	if tryBan && canBan {
 		if channel.bannedUntil.After(now) {
@@ -166,7 +176,10 @@ func (m *MemModelMonitor) GetModelsErrorRate(_ context.Context) (map[string]floa
 	return result, nil
 }
 
-func (m *MemModelMonitor) GetModelChannelErrorRate(_ context.Context, model string) (map[int64]float64, error) {
+func (m *MemModelMonitor) GetModelChannelErrorRate(
+	_ context.Context,
+	model string,
+) (map[int64]float64, error) {
 	m.mu.RLock()
 	defer m.mu.RUnlock()
 
@@ -179,7 +192,10 @@ func (m *MemModelMonitor) GetModelChannelErrorRate(_ context.Context, model stri
 	return result, nil
 }
 
-func (m *MemModelMonitor) GetChannelModelErrorRates(_ context.Context, channelID int64) (map[string]float64, error) {
+func (m *MemModelMonitor) GetChannelModelErrorRates(
+	_ context.Context,
+	channelID int64,
+) (map[string]float64, error) {
 	m.mu.RLock()
 	defer m.mu.RUnlock()
 
@@ -192,7 +208,9 @@ func (m *MemModelMonitor) GetChannelModelErrorRates(_ context.Context, channelID
 	return result, nil
 }
 
-func (m *MemModelMonitor) GetAllChannelModelErrorRates(_ context.Context) (map[int64]map[string]float64, error) {
+func (m *MemModelMonitor) GetAllChannelModelErrorRates(
+	_ context.Context,
+) (map[int64]map[string]float64, error) {
 	m.mu.RLock()
 	defer m.mu.RUnlock()
 
@@ -208,7 +226,10 @@ func (m *MemModelMonitor) GetAllChannelModelErrorRates(_ context.Context) (map[i
 	return result, nil
 }
 
-func (m *MemModelMonitor) GetBannedChannelsWithModel(_ context.Context, model string) ([]int64, error) {
+func (m *MemModelMonitor) GetBannedChannelsWithModel(
+	_ context.Context,
+	model string,
+) ([]int64, error) {
 	m.mu.RLock()
 	defer m.mu.RUnlock()
 
@@ -244,7 +265,11 @@ func (m *MemModelMonitor) GetAllBannedModelChannels(_ context.Context) (map[stri
 	return result, nil
 }
 
-func (m *MemModelMonitor) ClearChannelModelErrors(_ context.Context, model string, channelID int) error {
+func (m *MemModelMonitor) ClearChannelModelErrors(
+	_ context.Context,
+	model string,
+	channelID int,
+) error {
 	m.mu.Lock()
 	defer m.mu.Unlock()
 

+ 23 - 4
core/monitor/model.go

@@ -67,9 +67,21 @@ func GetModelsErrorRate(ctx context.Context) (map[string]float64, error) {
 }
 
 // AddRequest adds a request record and checks if channel should be banned
-func AddRequest(ctx context.Context, model string, channelID int64, isError, tryBan bool, maxErrorRate float64) (beyondThreshold bool, banExecution bool, err error) {
+func AddRequest(
+	ctx context.Context,
+	model string,
+	channelID int64,
+	isError, tryBan bool,
+	maxErrorRate float64,
+) (beyondThreshold, banExecution bool, err error) {
 	if !common.RedisEnabled {
-		beyondThreshold, banExecution = memModelMonitor.AddRequest(model, channelID, isError, tryBan, maxErrorRate)
+		beyondThreshold, banExecution = memModelMonitor.AddRequest(
+			model,
+			channelID,
+			isError,
+			tryBan,
+			maxErrorRate,
+		)
 		return beyondThreshold, banExecution, nil
 	}
 
@@ -98,8 +110,15 @@ func AddRequest(ctx context.Context, model string, channelID int64, isError, try
 	return val == 3, val == 1, nil
 }
 
-func buildStatsKey(model string, channelID string) string {
-	return fmt.Sprintf("%s%s%s%v%s", modelKeyPrefix, model, channelKeyPart, channelID, statsKeySuffix)
+func buildStatsKey(model, channelID string) string {
+	return fmt.Sprintf(
+		"%s%s%s%v%s",
+		modelKeyPrefix,
+		model,
+		channelKeyPart,
+		channelID,
+		statsKeySuffix,
+	)
 }
 
 func getModelChannelID(key string) (string, int64, bool) {

+ 29 - 6
core/relay/adaptor/ali/adaptor.go

@@ -58,7 +58,10 @@ func (a *Adaptor) SetupRequestHeader(meta *meta.Meta, _ *gin.Context, req *http.
 	return nil
 }
 
-func (a *Adaptor) ConvertRequest(meta *meta.Meta, req *http.Request) (*adaptor.ConvertRequestResult, error) {
+func (a *Adaptor) ConvertRequest(
+	meta *meta.Meta,
+	req *http.Request,
+) (*adaptor.ConvertRequestResult, error) {
 	switch meta.Mode {
 	case mode.ImagesGenerations:
 		return ConvertImageRequest(meta, req)
@@ -75,7 +78,11 @@ func (a *Adaptor) ConvertRequest(meta *meta.Meta, req *http.Request) (*adaptor.C
 	}
 }
 
-func (a *Adaptor) DoRequest(meta *meta.Meta, _ *gin.Context, req *http.Request) (*http.Response, error) {
+func (a *Adaptor) DoRequest(
+	meta *meta.Meta,
+	_ *gin.Context,
+	req *http.Request,
+) (*http.Response, error) {
 	switch meta.Mode {
 	case mode.AudioSpeech:
 		return TTSDoRequest(meta, req)
@@ -88,7 +95,11 @@ func (a *Adaptor) DoRequest(meta *meta.Meta, _ *gin.Context, req *http.Request)
 	}
 }
 
-func (a *Adaptor) DoResponse(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model.Usage, adaptor.Error) {
+func (a *Adaptor) DoResponse(
+	meta *meta.Meta,
+	c *gin.Context,
+	resp *http.Response,
+) (*model.Usage, adaptor.Error) {
 	switch meta.Mode {
 	case mode.ImagesGenerations:
 		return ImageHandler(meta, c, resp)
@@ -97,11 +108,19 @@ func (a *Adaptor) DoResponse(meta *meta.Meta, c *gin.Context, resp *http.Respons
 	case mode.ChatCompletions:
 		reqBody, err := common.GetRequestBody(c.Request)
 		if err != nil {
-			return nil, relaymodel.WrapperOpenAIErrorWithMessage(fmt.Sprintf("get request body failed: %s", err), "get_request_body_failed", http.StatusInternalServerError)
+			return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+				fmt.Sprintf("get request body failed: %s", err),
+				"get_request_body_failed",
+				http.StatusInternalServerError,
+			)
 		}
 		enableSearch, err := getEnableSearch(reqBody)
 		if err != nil {
-			return nil, relaymodel.WrapperOpenAIErrorWithMessage(fmt.Sprintf("get enable_search failed: %s", err), "get_enable_search_failed", http.StatusInternalServerError)
+			return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+				fmt.Sprintf("get enable_search failed: %s", err),
+				"get_enable_search_failed",
+				http.StatusInternalServerError,
+			)
 		}
 		u, e := openai.DoResponse(meta, c, resp)
 		if e != nil {
@@ -118,7 +137,11 @@ func (a *Adaptor) DoResponse(meta *meta.Meta, c *gin.Context, resp *http.Respons
 	case mode.AudioTranscription:
 		return STTDoResponse(meta, c, resp)
 	default:
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(fmt.Sprintf("unsupported mode: %s", meta.Mode), "unsupported_mode", http.StatusBadRequest)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			fmt.Sprintf("unsupported mode: %s", meta.Mode),
+			"unsupported_mode",
+			http.StatusBadRequest,
+		)
 	}
 }
 

+ 3 - 1
core/relay/adaptor/ali/constants.go

@@ -954,7 +954,9 @@ var ModelList = []model.ModelConfig{
 		},
 		Config: model.NewModelConfig(
 			model.WithModelConfigMaxInputTokens(10000),
-			model.WithModelConfigSupportFormats([]string{"pcm", "wav", "opus", "speex", "aac", "amr"}),
+			model.WithModelConfigSupportFormats(
+				[]string{"pcm", "wav", "opus", "speex", "aac", "amr"},
+			),
 		),
 	},
 

+ 32 - 11
core/relay/adaptor/ali/embeddings.go

@@ -19,7 +19,10 @@ import (
 // Deprecated: Use openai.ConvertRequest instead
 // /api/v1/services/embeddings/text-embedding/text-embedding
 
-func ConvertEmbeddingsRequest(meta *meta.Meta, req *http.Request) (string, http.Header, io.Reader, error) {
+func ConvertEmbeddingsRequest(
+	meta *meta.Meta,
+	req *http.Request,
+) (string, http.Header, io.Reader, error) {
 	var reqMap map[string]any
 	err := common.UnmarshalBodyReusable(req, &reqMap)
 	if err != nil {
@@ -56,7 +59,10 @@ func ConvertEmbeddingsRequest(meta *meta.Meta, req *http.Request) (string, http.
 	return http.MethodPost, nil, bytes.NewReader(jsonData), nil
 }
 
-func embeddingResponse2OpenAI(meta *meta.Meta, response *EmbeddingResponse) *relaymodel.EmbeddingResponse {
+func embeddingResponse2OpenAI(
+	meta *meta.Meta,
+	response *EmbeddingResponse,
+) *relaymodel.EmbeddingResponse {
 	openAIEmbeddingResponse := relaymodel.EmbeddingResponse{
 		Object: "list",
 		Data:   make([]*relaymodel.EmbeddingResponseItem, 0, 1),
@@ -65,16 +71,23 @@ func embeddingResponse2OpenAI(meta *meta.Meta, response *EmbeddingResponse) *rel
 	}
 
 	for i, embedding := range response.Output.Embeddings {
-		openAIEmbeddingResponse.Data = append(openAIEmbeddingResponse.Data, &relaymodel.EmbeddingResponseItem{
-			Object:    "embedding",
-			Index:     i,
-			Embedding: embedding.Embedding,
-		})
+		openAIEmbeddingResponse.Data = append(
+			openAIEmbeddingResponse.Data,
+			&relaymodel.EmbeddingResponseItem{
+				Object:    "embedding",
+				Index:     i,
+				Embedding: embedding.Embedding,
+			},
+		)
 	}
 	return &openAIEmbeddingResponse
 }
 
-func EmbeddingsHandler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model.Usage, adaptor.Error) {
+func EmbeddingsHandler(
+	meta *meta.Meta,
+	c *gin.Context,
+	resp *http.Response,
+) (*model.Usage, adaptor.Error) {
 	defer resp.Body.Close()
 
 	log := middleware.GetLogger(c)
@@ -86,7 +99,11 @@ func EmbeddingsHandler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*m
 	var respBody EmbeddingResponse
 	err = sonic.Unmarshal(responseBody, &respBody)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIError(err, "unmarshal_response_body_failed", resp.StatusCode)
+		return nil, relaymodel.WrapperOpenAIError(
+			err,
+			"unmarshal_response_body_failed",
+			resp.StatusCode,
+		)
 	}
 	if respBody.Usage.PromptTokens == 0 {
 		respBody.Usage.PromptTokens = respBody.Usage.TotalTokens
@@ -94,11 +111,15 @@ func EmbeddingsHandler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*m
 	openaiResponse := embeddingResponse2OpenAI(meta, &respBody)
 	data, err := sonic.Marshal(openaiResponse)
 	if err != nil {
-		return openaiResponse.Usage.ToModelUsage(), relaymodel.WrapperOpenAIError(err, "marshal_response_body_failed", resp.StatusCode)
+		return openaiResponse.ToModelUsage(), relaymodel.WrapperOpenAIError(
+			err,
+			"marshal_response_body_failed",
+			resp.StatusCode,
+		)
 	}
 	_, err = c.Writer.Write(data)
 	if err != nil {
 		log.Warnf("write response body failed: %v", err)
 	}
-	return openaiResponse.Usage.ToModelUsage(), nil
+	return openaiResponse.ToModelUsage(), nil
 }

+ 47 - 12
core/relay/adaptor/ali/image.go

@@ -24,7 +24,10 @@ import (
 
 const MetaResponseFormat = "response_format"
 
-func ConvertImageRequest(meta *meta.Meta, req *http.Request) (*adaptor.ConvertRequestResult, error) {
+func ConvertImageRequest(
+	meta *meta.Meta,
+	req *http.Request,
+) (*adaptor.ConvertRequestResult, error) {
 	request, err := utils.UnmarshalImageRequest(req)
 	if err != nil {
 		return nil, err
@@ -53,7 +56,11 @@ func ConvertImageRequest(meta *meta.Meta, req *http.Request) (*adaptor.ConvertRe
 	}, nil
 }
 
-func ImageHandler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model.Usage, adaptor.Error) {
+func ImageHandler(
+	meta *meta.Meta,
+	c *gin.Context,
+	resp *http.Response,
+) (*model.Usage, adaptor.Error) {
 	if resp.StatusCode != http.StatusOK {
 		return nil, openai.ErrorHanlder(resp)
 	}
@@ -62,36 +69,60 @@ func ImageHandler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model.
 
 	log := middleware.GetLogger(c)
 
-	responseFormat := meta.MustGet(MetaResponseFormat).(string)
+	responseFormat, _ := meta.MustGet(MetaResponseFormat).(string)
 
 	var aliTaskResponse TaskResponse
 	responseBody, err := io.ReadAll(resp.Body)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIError(err, "read_response_body_failed", http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIError(
+			err,
+			"read_response_body_failed",
+			http.StatusInternalServerError,
+		)
 	}
 	err = sonic.Unmarshal(responseBody, &aliTaskResponse)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIError(err, "unmarshal_response_body_failed", http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIError(
+			err,
+			"unmarshal_response_body_failed",
+			http.StatusInternalServerError,
+		)
 	}
 
 	if aliTaskResponse.Message != "" {
 		log.Error("aliAsyncTask err: " + aliTaskResponse.Message)
-		return nil, relaymodel.WrapperOpenAIError(errors.New(aliTaskResponse.Message), "ali_async_task_failed", http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIError(
+			errors.New(aliTaskResponse.Message),
+			"ali_async_task_failed",
+			http.StatusInternalServerError,
+		)
 	}
 
 	aliResponse, err := asyncTaskWait(c, aliTaskResponse.Output.TaskID, meta.Channel.Key)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIError(err, "ali_async_task_wait_failed", http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIError(
+			err,
+			"ali_async_task_wait_failed",
+			http.StatusInternalServerError,
+		)
 	}
 
 	if aliResponse.Output.TaskStatus != "SUCCEEDED" {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(aliResponse.Output.Message, "ali_error", resp.StatusCode)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			aliResponse.Output.Message,
+			"ali_error",
+			resp.StatusCode,
+		)
 	}
 
 	fullTextResponse := responseAli2OpenAIImage(c.Request.Context(), aliResponse, responseFormat)
 	jsonResponse, err := sonic.Marshal(fullTextResponse)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIError(err, "marshal_response_body_failed", http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIError(
+			err,
+			"marshal_response_body_failed",
+			http.StatusInternalServerError,
+		)
 	}
 	c.Writer.Header().Set("Content-Type", "application/json")
 	c.Writer.WriteHeader(resp.StatusCode)
@@ -102,7 +133,7 @@ func ImageHandler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model.
 	}, nil
 }
 
-func asyncTask(ctx context.Context, taskID string, key string) (*TaskResponse, error) {
+func asyncTask(ctx context.Context, taskID, key string) (*TaskResponse, error) {
 	url := "https://dashscope.aliyuncs.com/api/v1/tasks/" + taskID
 
 	var aliResponse TaskResponse
@@ -130,7 +161,7 @@ func asyncTask(ctx context.Context, taskID string, key string) (*TaskResponse, e
 	return &response, nil
 }
 
-func asyncTaskWait(ctx context.Context, taskID string, key string) (*TaskResponse, error) {
+func asyncTaskWait(ctx context.Context, taskID, key string) (*TaskResponse, error) {
 	waitSeconds := 2
 	step := 0
 	maxStep := 20
@@ -165,7 +196,11 @@ func asyncTaskWait(ctx context.Context, taskID string, key string) (*TaskRespons
 	return nil, errors.New("aliAsyncTaskWait timeout")
 }
 
-func responseAli2OpenAIImage(ctx context.Context, response *TaskResponse, responseFormat string) *relaymodel.ImageResponse {
+func responseAli2OpenAIImage(
+	ctx context.Context,
+	response *TaskResponse,
+	responseFormat string,
+) *relaymodel.ImageResponse {
 	imageResponse := relaymodel.ImageResponse{
 		Created: time.Now().Unix(),
 	}

+ 24 - 5
core/relay/adaptor/ali/rerank.go

@@ -28,7 +28,10 @@ type RerankUsage struct {
 	TotalTokens int64 `json:"total_tokens"`
 }
 
-func ConvertRerankRequest(meta *meta.Meta, req *http.Request) (*adaptor.ConvertRequestResult, error) {
+func ConvertRerankRequest(
+	meta *meta.Meta,
+	req *http.Request,
+) (*adaptor.ConvertRequestResult, error) {
 	reqMap := make(map[string]any)
 	err := common.UnmarshalBodyReusable(req, &reqMap)
 	if err != nil {
@@ -61,7 +64,11 @@ func ConvertRerankRequest(meta *meta.Meta, req *http.Request) (*adaptor.ConvertR
 	}, nil
 }
 
-func RerankHandler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model.Usage, adaptor.Error) {
+func RerankHandler(
+	meta *meta.Meta,
+	c *gin.Context,
+	resp *http.Response,
+) (*model.Usage, adaptor.Error) {
 	if resp.StatusCode != http.StatusOK {
 		return nil, openai.ErrorHanlder(resp)
 	}
@@ -72,12 +79,20 @@ func RerankHandler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model
 
 	responseBody, err := io.ReadAll(resp.Body)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIError(err, "read_response_body_failed", http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIError(
+			err,
+			"read_response_body_failed",
+			http.StatusInternalServerError,
+		)
 	}
 	var rerankResponse RerankResponse
 	err = sonic.Unmarshal(responseBody, &rerankResponse)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIError(err, "unmarshal_response_body_failed", http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIError(
+			err,
+			"unmarshal_response_body_failed",
+			http.StatusInternalServerError,
+		)
 	}
 
 	c.Writer.WriteHeader(resp.StatusCode)
@@ -108,7 +123,11 @@ func RerankHandler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model
 
 	jsonResponse, err := sonic.Marshal(&rerankResp)
 	if err != nil {
-		return usage, relaymodel.WrapperOpenAIError(err, "marshal_response_body_failed", http.StatusInternalServerError)
+		return usage, relaymodel.WrapperOpenAIError(
+			err,
+			"marshal_response_body_failed",
+			http.StatusInternalServerError,
+		)
 	}
 	_, err = c.Writer.Write(jsonResponse)
 	if err != nil {

+ 57 - 13
core/relay/adaptor/ali/stt-realtime.go

@@ -2,6 +2,7 @@ package ali
 
 import (
 	"bytes"
+	"fmt"
 	"io"
 	"net/http"
 	"strconv"
@@ -65,7 +66,10 @@ type STTUsage struct {
 	Characters int64 `json:"characters"`
 }
 
-func ConvertSTTRequest(meta *meta.Meta, request *http.Request) (*adaptor.ConvertRequestResult, error) {
+func ConvertSTTRequest(
+	meta *meta.Meta,
+	request *http.Request,
+) (*adaptor.ConvertRequestResult, error) {
 	err := request.ParseMultipartForm(1024 * 1024 * 4)
 	if err != nil {
 		return nil, err
@@ -150,11 +154,23 @@ func STTDoRequest(meta *meta.Meta, req *http.Request) (*http.Response, error) {
 	}, nil
 }
 
-func STTDoResponse(meta *meta.Meta, c *gin.Context, _ *http.Response) (usage *model.Usage, err adaptor.Error) {
-	audioData := meta.MustGet("audio_data").([]byte)
-	taskID := meta.MustGet("task_id").(string)
-
-	conn := meta.MustGet("ws_conn").(*websocket.Conn)
+func STTDoResponse(
+	meta *meta.Meta,
+	c *gin.Context,
+	_ *http.Response,
+) (usage *model.Usage, err adaptor.Error) {
+	audioData, ok := meta.MustGet("audio_data").([]byte)
+	if !ok {
+		panic(fmt.Sprintf("audio data type error: %T, %v", audioData, audioData))
+	}
+	taskID, ok := meta.MustGet("task_id").(string)
+	if !ok {
+		panic(fmt.Sprintf("task id type error: %T, %v", taskID, taskID))
+	}
+	conn, ok := meta.MustGet("ws_conn").(*websocket.Conn)
+	if !ok {
+		panic(fmt.Sprintf("ws conn type error: %T, %v", conn, conn))
+	}
 	defer conn.Close()
 
 	output := strings.Builder{}
@@ -164,17 +180,29 @@ func STTDoResponse(meta *meta.Meta, c *gin.Context, _ *http.Response) (usage *mo
 	for {
 		messageType, data, err := conn.ReadMessage()
 		if err != nil {
-			return usage, relaymodel.WrapperOpenAIErrorWithMessage("ali_wss_read_msg_failed", nil, http.StatusInternalServerError)
+			return usage, relaymodel.WrapperOpenAIErrorWithMessage(
+				"ali_wss_read_msg_failed",
+				nil,
+				http.StatusInternalServerError,
+			)
 		}
 
 		if messageType != websocket.TextMessage {
-			return usage, relaymodel.WrapperOpenAIErrorWithMessage("expect text message, but got binary message", nil, http.StatusInternalServerError)
+			return usage, relaymodel.WrapperOpenAIErrorWithMessage(
+				"expect text message, but got binary message",
+				nil,
+				http.StatusInternalServerError,
+			)
 		}
 
 		var msg STTMessage
 		err = sonic.Unmarshal(data, &msg)
 		if err != nil {
-			return usage, relaymodel.WrapperOpenAIErrorWithMessage("ali_wss_read_msg_failed", nil, http.StatusInternalServerError)
+			return usage, relaymodel.WrapperOpenAIErrorWithMessage(
+				"ali_wss_read_msg_failed",
+				nil,
+				http.StatusInternalServerError,
+			)
 		}
 		switch msg.Header.Event {
 		case "task-started":
@@ -187,7 +215,11 @@ func STTDoResponse(meta *meta.Meta, c *gin.Context, _ *http.Response) (usage *mo
 				chunk := audioData[i:end]
 				err = conn.WriteMessage(websocket.BinaryMessage, chunk)
 				if err != nil {
-					return usage, relaymodel.WrapperOpenAIErrorWithMessage("ali_wss_write_msg_failed", nil, http.StatusInternalServerError)
+					return usage, relaymodel.WrapperOpenAIErrorWithMessage(
+						"ali_wss_write_msg_failed",
+						nil,
+						http.StatusInternalServerError,
+					)
 				}
 			}
 			finishMsg := STTMessage{
@@ -202,11 +234,19 @@ func STTDoResponse(meta *meta.Meta, c *gin.Context, _ *http.Response) (usage *mo
 			}
 			finishData, err := sonic.Marshal(finishMsg)
 			if err != nil {
-				return usage, relaymodel.WrapperOpenAIErrorWithMessage("ali_wss_write_msg_failed", nil, http.StatusInternalServerError)
+				return usage, relaymodel.WrapperOpenAIErrorWithMessage(
+					"ali_wss_write_msg_failed",
+					nil,
+					http.StatusInternalServerError,
+				)
 			}
 			err = conn.WriteMessage(websocket.TextMessage, finishData)
 			if err != nil {
-				return usage, relaymodel.WrapperOpenAIErrorWithMessage("ali_wss_write_msg_failed", nil, http.StatusInternalServerError)
+				return usage, relaymodel.WrapperOpenAIErrorWithMessage(
+					"ali_wss_write_msg_failed",
+					nil,
+					http.StatusInternalServerError,
+				)
 			}
 		case "result-generated":
 			if msg.Payload.Output.STTSentence.EndTime != nil &&
@@ -226,7 +266,11 @@ func STTDoResponse(meta *meta.Meta, c *gin.Context, _ *http.Response) (usage *mo
 			})
 			return usage, nil
 		case "task-failed":
-			return usage, relaymodel.WrapperOpenAIErrorWithMessage(msg.Header.ErrorMessage, msg.Header.ErrorCode, http.StatusInternalServerError)
+			return usage, relaymodel.WrapperOpenAIErrorWithMessage(
+				msg.Header.ErrorMessage,
+				msg.Header.ErrorCode,
+				http.StatusInternalServerError,
+			)
 		}
 	}
 }

+ 29 - 6
core/relay/adaptor/ali/tts.go

@@ -115,7 +115,11 @@ func ConvertTTSRequest(meta *meta.Meta, req *http.Request) (*adaptor.ConvertRequ
 		if voice == "" {
 			voice = "zhinan"
 		}
-		request.Model = fmt.Sprintf("sambert-%s-v%s", voice, strings.TrimPrefix(request.Model, "sambert-v"))
+		request.Model = fmt.Sprintf(
+			"sambert-%s-v%s",
+			voice,
+			strings.TrimPrefix(request.Model, "sambert-v"),
+		)
 	}
 
 	ttsRequest := TTSMessage{
@@ -195,10 +199,17 @@ func TTSDoRequest(meta *meta.Meta, req *http.Request) (*http.Response, error) {
 	}, nil
 }
 
-func TTSDoResponse(meta *meta.Meta, c *gin.Context, _ *http.Response) (usage *model.Usage, err adaptor.Error) {
+func TTSDoResponse(
+	meta *meta.Meta,
+	c *gin.Context,
+	_ *http.Response,
+) (usage *model.Usage, err adaptor.Error) {
 	log := middleware.GetLogger(c)
 
-	conn := meta.MustGet("ws_conn").(*websocket.Conn)
+	conn, ok := meta.MustGet("ws_conn").(*websocket.Conn)
+	if !ok {
+		panic(fmt.Sprintf("ws conn type error: %T, %v", conn, conn))
+	}
 	defer conn.Close()
 
 	usage = &model.Usage{}
@@ -206,7 +217,11 @@ func TTSDoResponse(meta *meta.Meta, c *gin.Context, _ *http.Response) (usage *mo
 	for {
 		messageType, data, err := conn.ReadMessage()
 		if err != nil {
-			return usage, relaymodel.WrapperOpenAIErrorWithMessage("ali_wss_read_msg_failed", nil, http.StatusInternalServerError)
+			return usage, relaymodel.WrapperOpenAIErrorWithMessage(
+				"ali_wss_read_msg_failed",
+				nil,
+				http.StatusInternalServerError,
+			)
 		}
 
 		var msg TTSMessage
@@ -214,7 +229,11 @@ func TTSDoResponse(meta *meta.Meta, c *gin.Context, _ *http.Response) (usage *mo
 		case websocket.TextMessage:
 			err = sonic.Unmarshal(data, &msg)
 			if err != nil {
-				return usage, relaymodel.WrapperOpenAIErrorWithMessage("ali_wss_read_msg_failed", nil, http.StatusInternalServerError)
+				return usage, relaymodel.WrapperOpenAIErrorWithMessage(
+					"ali_wss_read_msg_failed",
+					nil,
+					http.StatusInternalServerError,
+				)
 			}
 			switch msg.Header.Event {
 			case "task-started":
@@ -226,7 +245,11 @@ func TTSDoResponse(meta *meta.Meta, c *gin.Context, _ *http.Response) (usage *mo
 				usage.TotalTokens = model.ZeroNullInt64(msg.Payload.Usage.Characters)
 				return usage, nil
 			case "task-failed":
-				return usage, relaymodel.WrapperOpenAIErrorWithMessage(msg.Header.ErrorMessage, msg.Header.ErrorCode, http.StatusInternalServerError)
+				return usage, relaymodel.WrapperOpenAIErrorWithMessage(
+					msg.Header.ErrorMessage,
+					msg.Header.ErrorCode,
+					http.StatusInternalServerError,
+				)
 			}
 		case websocket.BinaryMessage:
 			_, writeErr := c.Writer.Write(data)

+ 19 - 4
core/relay/adaptor/anthropic/adaptor.go

@@ -57,7 +57,10 @@ func (a *Adaptor) SetupRequestHeader(meta *meta.Meta, c *gin.Context, req *http.
 	return nil
 }
 
-func (a *Adaptor) ConvertRequest(meta *meta.Meta, req *http.Request) (*adaptor.ConvertRequestResult, error) {
+func (a *Adaptor) ConvertRequest(
+	meta *meta.Meta,
+	req *http.Request,
+) (*adaptor.ConvertRequestResult, error) {
 	switch meta.Mode {
 	case mode.ChatCompletions:
 		data, err := OpenAIConvertRequest(meta, req)
@@ -81,11 +84,19 @@ func (a *Adaptor) ConvertRequest(meta *meta.Meta, req *http.Request) (*adaptor.C
 	}
 }
 
-func (a *Adaptor) DoRequest(_ *meta.Meta, _ *gin.Context, req *http.Request) (*http.Response, error) {
+func (a *Adaptor) DoRequest(
+	_ *meta.Meta,
+	_ *gin.Context,
+	req *http.Request,
+) (*http.Response, error) {
 	return utils.DoRequest(req)
 }
 
-func (a *Adaptor) DoResponse(meta *meta.Meta, c *gin.Context, resp *http.Response) (usage *model.Usage, err adaptor.Error) {
+func (a *Adaptor) DoResponse(
+	meta *meta.Meta,
+	c *gin.Context,
+	resp *http.Response,
+) (usage *model.Usage, err adaptor.Error) {
 	switch meta.Mode {
 	case mode.ChatCompletions:
 		if utils.IsStreamResponse(resp) {
@@ -100,7 +111,11 @@ func (a *Adaptor) DoResponse(meta *meta.Meta, c *gin.Context, resp *http.Respons
 			usage, err = Handler(meta, c, resp)
 		}
 	default:
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(fmt.Sprintf("unsupported mode: %s", meta.Mode), "unsupported_mode", http.StatusBadRequest)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			fmt.Sprintf("unsupported mode: %s", meta.Mode),
+			"unsupported_mode",
+			http.StatusBadRequest,
+		)
 	}
 	return
 }

+ 8 - 2
core/relay/adaptor/anthropic/error.go

@@ -14,7 +14,11 @@ func OpenAIErrorHandler(resp *http.Response) adaptor.Error {
 	defer resp.Body.Close()
 	respBody, err := io.ReadAll(resp.Body)
 	if err != nil {
-		return relaymodel.WrapperOpenAIError(err, "read_response_failed", http.StatusInternalServerError)
+		return relaymodel.WrapperOpenAIError(
+			err,
+			"read_response_failed",
+			http.StatusInternalServerError,
+		)
 	}
 
 	return OpenAIErrorHandlerWithBody(resp.StatusCode, respBody)
@@ -44,7 +48,9 @@ func GetError(resp *http.Response) (int, relaymodel.AnthropicError) {
 	return GetErrorWithBody(resp.StatusCode, respBody)
 }
 
-// status 400 {"type":"error","error":{"type":"invalid_request_error","message":"Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits."}}
+// status 400 {"type":"error","error":{"type":"invalid_request_error","message":"Your credit balance
+// is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase
+// credits."}}
 // status 529 {Message:Overloaded Type:overloaded_error Param:}
 func GetErrorWithBody(statusCode int, respBody []byte) (int, relaymodel.AnthropicError) {
 	var e relaymodel.AnthropicErrorResponse

+ 2 - 1
core/relay/adaptor/anthropic/event.go

@@ -50,7 +50,8 @@ func (r *Anthropic) Render(w http.ResponseWriter) error {
 		r.Data,
 		nnBytes,
 	} {
-		// nosemgrep: go.lang.security.audit.xss.no-direct-write-to-responsewriter.no-direct-write-to-responsewriter
+		// nosemgrep:
+		// go.lang.security.audit.xss.no-direct-write-to-responsewriter.no-direct-write-to-responsewriter
 		if _, err := w.Write(bytes); err != nil {
 			return err
 		}

+ 22 - 5
core/relay/adaptor/anthropic/main.go

@@ -156,7 +156,11 @@ func convertImageURLToBase64(ctx context.Context, contentItem *ast.Node) error {
 	return nil
 }
 
-func StreamHandler(m *meta.Meta, c *gin.Context, resp *http.Response) (*model.Usage, adaptor.Error) {
+func StreamHandler(
+	m *meta.Meta,
+	c *gin.Context,
+	resp *http.Response,
+) (*model.Usage, adaptor.Error) {
 	if resp.StatusCode != http.StatusOK {
 		return nil, ErrorHandler(resp)
 	}
@@ -224,7 +228,12 @@ func StreamHandler(m *meta.Meta, c *gin.Context, resp *http.Response) (*model.Us
 		usage = &relaymodel.Usage{
 			PromptTokens:     int64(m.RequestUsage.InputTokens),
 			CompletionTokens: openai.CountTokenText(responseText.String(), m.OriginModel),
-			TotalTokens:      int64(m.RequestUsage.InputTokens) + openai.CountTokenText(responseText.String(), m.OriginModel),
+			TotalTokens: int64(
+				m.RequestUsage.InputTokens,
+			) + openai.CountTokenText(
+				responseText.String(),
+				m.OriginModel,
+			),
 		}
 	}
 
@@ -240,17 +249,25 @@ func Handler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model.Usage
 
 	respBody, err := io.ReadAll(resp.Body)
 	if err != nil {
-		return nil, relaymodel.WrapperAnthropicError(err, "read_response_failed", http.StatusInternalServerError)
+		return nil, relaymodel.WrapperAnthropicError(
+			err,
+			"read_response_failed",
+			http.StatusInternalServerError,
+		)
 	}
 
 	var claudeResponse Response
 	err = sonic.Unmarshal(respBody, &claudeResponse)
 	if err != nil {
-		return nil, relaymodel.WrapperAnthropicError(err, "unmarshal_response_body_failed", http.StatusInternalServerError)
+		return nil, relaymodel.WrapperAnthropicError(
+			err,
+			"unmarshal_response_body_failed",
+			http.StatusInternalServerError,
+		)
 	}
 	fullTextResponse := Response2OpenAI(meta, &claudeResponse)
 	c.Writer.Header().Set("Content-Type", "application/json")
 	c.Writer.WriteHeader(resp.StatusCode)
 	_, _ = c.Writer.Write(respBody)
-	return fullTextResponse.Usage.ToModelUsage(), nil
+	return fullTextResponse.ToModelUsage(), nil
 }

+ 39 - 11
core/relay/adaptor/anthropic/openai.go

@@ -277,7 +277,10 @@ func batchPatchImage2Base64(ctx context.Context, imageTasks []*Content) error {
 }
 
 // https://docs.anthropic.com/claude/reference/messages-streaming
-func StreamResponse2OpenAI(meta *meta.Meta, respData []byte) (*relaymodel.ChatCompletionsStreamResponse, adaptor.Error) {
+func StreamResponse2OpenAI(
+	meta *meta.Meta,
+	respData []byte,
+) (*relaymodel.ChatCompletionsStreamResponse, adaptor.Error) {
 	var usage *relaymodel.Usage
 	var content string
 	var thinking string
@@ -287,7 +290,11 @@ func StreamResponse2OpenAI(meta *meta.Meta, respData []byte) (*relaymodel.ChatCo
 	var claudeResponse StreamResponse
 	err := sonic.Unmarshal(respData, &claudeResponse)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIError(err, "unmarshal_response", http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIError(
+			err,
+			"unmarshal_response",
+			http.StatusInternalServerError,
+		)
 	}
 
 	switch claudeResponse.Type {
@@ -417,14 +424,18 @@ func Response2OpenAI(meta *meta.Meta, claudeResponse *Response) *relaymodel.Text
 			},
 		},
 	}
-	if fullTextResponse.Usage.PromptTokens == 0 {
-		fullTextResponse.Usage.PromptTokens = int64(meta.RequestUsage.InputTokens)
+	if fullTextResponse.PromptTokens == 0 {
+		fullTextResponse.PromptTokens = int64(meta.RequestUsage.InputTokens)
 	}
-	fullTextResponse.Usage.TotalTokens = fullTextResponse.Usage.PromptTokens + fullTextResponse.Usage.CompletionTokens
+	fullTextResponse.TotalTokens = fullTextResponse.PromptTokens + fullTextResponse.CompletionTokens
 	return &fullTextResponse
 }
 
-func OpenAIStreamHandler(m *meta.Meta, c *gin.Context, resp *http.Response) (*model.Usage, adaptor.Error) {
+func OpenAIStreamHandler(
+	m *meta.Meta,
+	c *gin.Context,
+	resp *http.Response,
+) (*model.Usage, adaptor.Error) {
 	if resp.StatusCode != http.StatusOK {
 		return nil, OpenAIErrorHandler(resp)
 	}
@@ -498,7 +509,12 @@ func OpenAIStreamHandler(m *meta.Meta, c *gin.Context, resp *http.Response) (*mo
 		usage = &relaymodel.Usage{
 			PromptTokens:     int64(m.RequestUsage.InputTokens),
 			CompletionTokens: openai.CountTokenText(responseText.String(), m.OriginModel),
-			TotalTokens:      int64(m.RequestUsage.InputTokens) + openai.CountTokenText(responseText.String(), m.OriginModel),
+			TotalTokens: int64(
+				m.RequestUsage.InputTokens,
+			) + openai.CountTokenText(
+				responseText.String(),
+				m.OriginModel,
+			),
 		}
 		_ = render.ObjectData(c, &relaymodel.ChatCompletionsStreamResponse{
 			ID:      openai.ChatCompletionID(),
@@ -515,7 +531,11 @@ func OpenAIStreamHandler(m *meta.Meta, c *gin.Context, resp *http.Response) (*mo
 	return usage.ToModelUsage(), nil
 }
 
-func OpenAIHandler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model.Usage, adaptor.Error) {
+func OpenAIHandler(
+	meta *meta.Meta,
+	c *gin.Context,
+	resp *http.Response,
+) (*model.Usage, adaptor.Error) {
 	if resp.StatusCode != http.StatusOK {
 		return nil, OpenAIErrorHandler(resp)
 	}
@@ -525,15 +545,23 @@ func OpenAIHandler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model
 	var claudeResponse Response
 	err := sonic.ConfigDefault.NewDecoder(resp.Body).Decode(&claudeResponse)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIError(err, "unmarshal_response_body_failed", http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIError(
+			err,
+			"unmarshal_response_body_failed",
+			http.StatusInternalServerError,
+		)
 	}
 	fullTextResponse := Response2OpenAI(meta, &claudeResponse)
 	jsonResponse, err := sonic.Marshal(fullTextResponse)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIError(err, "marshal_response_body_failed", http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIError(
+			err,
+			"marshal_response_body_failed",
+			http.StatusInternalServerError,
+		)
 	}
 	c.Writer.Header().Set("Content-Type", "application/json")
 	c.Writer.WriteHeader(resp.StatusCode)
 	_, _ = c.Writer.Write(jsonResponse)
-	return fullTextResponse.Usage.ToModelUsage(), nil
+	return fullTextResponse.ToModelUsage(), nil
 }

+ 20 - 4
core/relay/adaptor/aws/adaptor.go

@@ -2,6 +2,7 @@ package aws
 
 import (
 	"errors"
+	"fmt"
 	"net/http"
 
 	"github.com/gin-gonic/gin"
@@ -18,7 +19,10 @@ func (a *Adaptor) GetBaseURL() string {
 	return ""
 }
 
-func (a *Adaptor) ConvertRequest(meta *meta.Meta, req *http.Request) (*adaptor.ConvertRequestResult, error) {
+func (a *Adaptor) ConvertRequest(
+	meta *meta.Meta,
+	req *http.Request,
+) (*adaptor.ConvertRequestResult, error) {
 	adaptor := GetAdaptor(meta.ActualModel)
 	if adaptor == nil {
 		return nil, errors.New("adaptor not found")
@@ -27,12 +31,24 @@ func (a *Adaptor) ConvertRequest(meta *meta.Meta, req *http.Request) (*adaptor.C
 	return adaptor.ConvertRequest(meta, req)
 }
 
-func (a *Adaptor) DoResponse(meta *meta.Meta, c *gin.Context, _ *http.Response) (usage *model.Usage, err adaptor.Error) {
+func (a *Adaptor) DoResponse(
+	meta *meta.Meta,
+	c *gin.Context,
+	_ *http.Response,
+) (usage *model.Usage, err adaptor.Error) {
 	adaptor, ok := meta.Get("awsAdapter")
 	if !ok {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage("awsAdapter not found", nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			"awsAdapter not found",
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
-	return adaptor.(utils.AwsAdapter).DoResponse(meta, c)
+	v, ok := adaptor.(utils.AwsAdapter)
+	if !ok {
+		panic(fmt.Sprintf("aws adapter type error: %T, %v", v, v))
+	}
+	return v.DoResponse(meta, c)
 }
 
 func (a *Adaptor) GetModelList() (models []model.ModelConfig) {

+ 8 - 2
core/relay/adaptor/aws/claude/adapter.go

@@ -16,7 +16,10 @@ const (
 
 type Adaptor struct{}
 
-func (a *Adaptor) ConvertRequest(meta *meta.Meta, req *http.Request) (*adaptor.ConvertRequestResult, error) {
+func (a *Adaptor) ConvertRequest(
+	meta *meta.Meta,
+	req *http.Request,
+) (*adaptor.ConvertRequestResult, error) {
 	r, err := anthropic.OpenAIConvertRequest(meta, req)
 	if err != nil {
 		return nil, err
@@ -30,7 +33,10 @@ func (a *Adaptor) ConvertRequest(meta *meta.Meta, req *http.Request) (*adaptor.C
 	}, nil
 }
 
-func (a *Adaptor) DoResponse(meta *meta.Meta, c *gin.Context) (usage *model.Usage, err adaptor.Error) {
+func (a *Adaptor) DoResponse(
+	meta *meta.Meta,
+	c *gin.Context,
+) (usage *model.Usage, err adaptor.Error) {
 	if meta.GetBool("stream") {
 		usage, err = StreamHandler(meta, c)
 	} else {

+ 78 - 18
core/relay/adaptor/aws/claude/main.go

@@ -2,6 +2,7 @@
 package aws
 
 import (
+	"fmt"
 	"net/http"
 	"strings"
 	"time"
@@ -95,7 +96,11 @@ func awsModelID(requestModel string) (string, error) {
 func Handler(meta *meta.Meta, c *gin.Context) (*model.Usage, adaptor.Error) {
 	awsModelID, err := awsModelID(meta.ActualModel)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(err.Error(), nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			err.Error(),
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 
 	awsReq := &bedrockruntime.InvokeModelInput{
@@ -106,47 +111,78 @@ func Handler(meta *meta.Meta, c *gin.Context) (*model.Usage, adaptor.Error) {
 
 	convReq, ok := meta.Get(ConvertedRequest)
 	if !ok {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage("request not found", nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			"request not found",
+			nil,
+			http.StatusInternalServerError,
+		)
+	}
+	claudeReq, ok := convReq.(*anthropic.Request)
+	if !ok {
+		panic(fmt.Sprintf("claude request type error: %T, %v", claudeReq, claudeReq))
 	}
-	claudeReq := convReq.(*anthropic.Request)
 	awsClaudeReq := &Request{
 		AnthropicVersion: "bedrock-2023-05-31",
 	}
 	if err = copier.Copy(awsClaudeReq, claudeReq); err != nil {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(err.Error(), nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			err.Error(),
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 
 	awsReq.Body, err = sonic.Marshal(awsClaudeReq)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(err.Error(), nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			err.Error(),
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 
 	awsClient, err := utils.AwsClientFromMeta(meta)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(err.Error(), nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			err.Error(),
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 
 	awsResp, err := awsClient.InvokeModel(c.Request.Context(), awsReq)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(err.Error(), nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			err.Error(),
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 
 	claudeResponse := new(anthropic.Response)
 	err = sonic.Unmarshal(awsResp.Body, claudeResponse)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(err.Error(), nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			err.Error(),
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 
 	openaiResp := anthropic.Response2OpenAI(meta, claudeResponse)
 	c.JSON(http.StatusOK, openaiResp)
-	return openaiResp.Usage.ToModelUsage(), nil
+	return openaiResp.ToModelUsage(), nil
 }
 
 func StreamHandler(m *meta.Meta, c *gin.Context) (*model.Usage, adaptor.Error) {
 	log := middleware.GetLogger(c)
 	awsModelID, err := awsModelID(m.ActualModel)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(err.Error(), nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			err.Error(),
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 
 	awsReq := &bedrockruntime.InvokeModelWithResponseStreamInput{
@@ -157,32 +193,51 @@ func StreamHandler(m *meta.Meta, c *gin.Context) (*model.Usage, adaptor.Error) {
 
 	convReq, ok := m.Get(ConvertedRequest)
 	if !ok {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage("request not found", nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			"request not found",
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 	claudeReq, ok := convReq.(*anthropic.Request)
 	if !ok {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage("request not found", nil, http.StatusInternalServerError)
+		panic(fmt.Sprintf("claude request type error: %T, %v", claudeReq, claudeReq))
 	}
-
 	awsClaudeReq := &Request{
 		AnthropicVersion: "bedrock-2023-05-31",
 	}
 	if err = copier.Copy(awsClaudeReq, claudeReq); err != nil {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(err.Error(), nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			err.Error(),
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 	awsReq.Body, err = sonic.Marshal(awsClaudeReq)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(err.Error(), nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			err.Error(),
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 
 	awsClient, err := utils.AwsClientFromMeta(m)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(err.Error(), nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			err.Error(),
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 
 	awsResp, err := awsClient.InvokeModelWithResponseStream(c.Request.Context(), awsReq)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(err.Error(), nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			err.Error(),
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 	stream := awsResp.GetStream()
 	defer stream.Close()
@@ -242,7 +297,12 @@ func StreamHandler(m *meta.Meta, c *gin.Context) (*model.Usage, adaptor.Error) {
 		usage = &relaymodel.Usage{
 			PromptTokens:     int64(m.RequestUsage.InputTokens),
 			CompletionTokens: openai.CountTokenText(responseText.String(), m.OriginModel),
-			TotalTokens:      int64(m.RequestUsage.InputTokens) + openai.CountTokenText(responseText.String(), m.OriginModel),
+			TotalTokens: int64(
+				m.RequestUsage.InputTokens,
+			) + openai.CountTokenText(
+				responseText.String(),
+				m.OriginModel,
+			),
 		}
 		_ = render.ObjectData(c, &relaymodel.ChatCompletionsStreamResponse{
 			ID:      openai.ChatCompletionID(),

+ 8 - 2
core/relay/adaptor/aws/llama3/adapter.go

@@ -16,7 +16,10 @@ const (
 
 type Adaptor struct{}
 
-func (a *Adaptor) ConvertRequest(meta *meta.Meta, req *http.Request) (*adaptor.ConvertRequestResult, error) {
+func (a *Adaptor) ConvertRequest(
+	meta *meta.Meta,
+	req *http.Request,
+) (*adaptor.ConvertRequestResult, error) {
 	request, err := relayutils.UnmarshalGeneralOpenAIRequest(req)
 	if err != nil {
 		return nil, err
@@ -32,7 +35,10 @@ func (a *Adaptor) ConvertRequest(meta *meta.Meta, req *http.Request) (*adaptor.C
 	}, nil
 }
 
-func (a *Adaptor) DoResponse(meta *meta.Meta, c *gin.Context) (usage *model.Usage, err adaptor.Error) {
+func (a *Adaptor) DoResponse(
+	meta *meta.Meta,
+	c *gin.Context,
+) (usage *model.Usage, err adaptor.Error) {
 	if meta.GetBool("stream") {
 		usage, err = StreamHandler(meta, c)
 	} else {

+ 58 - 12
core/relay/adaptor/aws/llama3/main.go

@@ -93,7 +93,11 @@ func ConvertRequest(textRequest *relaymodel.GeneralOpenAIRequest) *Request {
 func Handler(meta *meta.Meta, c *gin.Context) (*model.Usage, adaptor.Error) {
 	awsModelID, err := awsModelID(meta.ActualModel)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(err.Error(), nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			err.Error(),
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 
 	awsReq := &bedrockruntime.InvokeModelInput{
@@ -104,28 +108,48 @@ func Handler(meta *meta.Meta, c *gin.Context) (*model.Usage, adaptor.Error) {
 
 	llamaReq, ok := meta.Get(ConvertedRequest)
 	if !ok {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage("request not found", nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			"request not found",
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 
 	awsReq.Body, err = sonic.Marshal(llamaReq)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(err.Error(), nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			err.Error(),
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 
 	awsClient, err := utils.AwsClientFromMeta(meta)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(err.Error(), nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			err.Error(),
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 
 	awsResp, err := awsClient.InvokeModel(c.Request.Context(), awsReq)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(err.Error(), nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			err.Error(),
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 
 	var llamaResponse Response
 	err = sonic.Unmarshal(awsResp.Body, &llamaResponse)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(err.Error(), nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			err.Error(),
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 
 	openaiResp := ResponseLlama2OpenAI(&llamaResponse)
@@ -170,7 +194,11 @@ func StreamHandler(meta *meta.Meta, c *gin.Context) (*model.Usage, adaptor.Error
 	createdTime := time.Now().Unix()
 	awsModelID, err := awsModelID(meta.ActualModel)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(err.Error(), nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			err.Error(),
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 
 	awsReq := &bedrockruntime.InvokeModelWithResponseStreamInput{
@@ -181,22 +209,38 @@ func StreamHandler(meta *meta.Meta, c *gin.Context) (*model.Usage, adaptor.Error
 
 	llamaReq, ok := meta.Get(ConvertedRequest)
 	if !ok {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage("request not found", nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			"request not found",
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 
 	awsReq.Body, err = sonic.Marshal(llamaReq)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(err.Error(), nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			err.Error(),
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 
 	awsClient, err := utils.AwsClientFromMeta(meta)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(err.Error(), nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			err.Error(),
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 
 	awsResp, err := awsClient.InvokeModelWithResponseStream(c.Request.Context(), awsReq)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(err.Error(), nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			err.Error(),
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 	stream := awsResp.GetStream()
 	defer stream.Close()
@@ -248,7 +292,9 @@ func StreamHandler(meta *meta.Meta, c *gin.Context) (*model.Usage, adaptor.Error
 	return usage.ToModelUsage(), nil
 }
 
-func StreamResponseLlama2OpenAI(llamaResponse *StreamResponse) *relaymodel.ChatCompletionsStreamResponse {
+func StreamResponseLlama2OpenAI(
+	llamaResponse *StreamResponse,
+) *relaymodel.ChatCompletionsStreamResponse {
 	var choice relaymodel.ChatCompletionsStreamResponseChoice
 	choice.Delta.Content = llamaResponse.Generation
 	choice.Delta.Role = "assistant"

+ 10 - 3
core/relay/adaptor/aws/utils/adaptor.go

@@ -2,6 +2,7 @@ package utils
 
 import (
 	"errors"
+	"fmt"
 	"net/http"
 	"strings"
 
@@ -39,8 +40,10 @@ func GetAwsConfigFromKey(key string) (*AwsConfig, error) {
 
 func AwsClient(config *AwsConfig) *bedrockruntime.Client {
 	return bedrockruntime.New(bedrockruntime.Options{
-		Region:      config.Region,
-		Credentials: aws.NewCredentialsCache(credentials.NewStaticCredentialsProvider(config.AK, config.SK, "")),
+		Region: config.Region,
+		Credentials: aws.NewCredentialsCache(
+			credentials.NewStaticCredentialsProvider(config.AK, config.SK, ""),
+		),
 	})
 }
 
@@ -57,7 +60,11 @@ const AwsClientKey = "aws_client"
 func AwsClientFromMeta(meta *meta.Meta) (*bedrockruntime.Client, error) {
 	awsClientI, ok := meta.Get(AwsClientKey)
 	if ok {
-		return awsClientI.(*bedrockruntime.Client), nil
+		v, ok := awsClientI.(*bedrockruntime.Client)
+		if !ok {
+			panic(fmt.Sprintf("aws client type error: %T, %v", v, v))
+		}
+		return v, nil
 	}
 	awsClient, err := awsClientFromKey(meta.Channel.Key)
 	if err != nil {

+ 36 - 6
core/relay/adaptor/azure/main.go

@@ -29,20 +29,50 @@ func (a *Adaptor) GetRequestURL(meta *meta.Meta) (string, error) {
 	case mode.ImagesGenerations:
 		// https://learn.microsoft.com/en-us/azure/ai-services/openai/dall-e-quickstart?tabs=dalle3%2Ccommand-line&pivots=rest-api
 		// https://{resource_name}.openai.azure.com/openai/deployments/dall-e-3/images/generations?api-version=2024-03-01-preview
-		return fmt.Sprintf("%s/openai/deployments/%s/images/generations?api-version=%s", meta.Channel.BaseURL, model, apiVersion), nil
+		return fmt.Sprintf(
+			"%s/openai/deployments/%s/images/generations?api-version=%s",
+			meta.Channel.BaseURL,
+			model,
+			apiVersion,
+		), nil
 	case mode.AudioTranscription:
 		// https://learn.microsoft.com/en-us/azure/ai-services/openai/whisper-quickstart?tabs=command-line#rest-api
-		return fmt.Sprintf("%s/openai/deployments/%s/audio/transcriptions?api-version=%s", meta.Channel.BaseURL, model, apiVersion), nil
+		return fmt.Sprintf(
+			"%s/openai/deployments/%s/audio/transcriptions?api-version=%s",
+			meta.Channel.BaseURL,
+			model,
+			apiVersion,
+		), nil
 	case mode.AudioSpeech:
 		// https://learn.microsoft.com/en-us/azure/ai-services/openai/text-to-speech-quickstart?tabs=command-line#rest-api
-		return fmt.Sprintf("%s/openai/deployments/%s/audio/speech?api-version=%s", meta.Channel.BaseURL, model, apiVersion), nil
+		return fmt.Sprintf(
+			"%s/openai/deployments/%s/audio/speech?api-version=%s",
+			meta.Channel.BaseURL,
+			model,
+			apiVersion,
+		), nil
 	case mode.ChatCompletions:
 		// https://learn.microsoft.com/en-us/azure/cognitive-services/openai/chatgpt-quickstart?pivots=rest-api&tabs=command-line#rest-api
-		return fmt.Sprintf("%s/openai/deployments/%s/chat/completions?api-version=%s", meta.Channel.BaseURL, model, apiVersion), nil
+		return fmt.Sprintf(
+			"%s/openai/deployments/%s/chat/completions?api-version=%s",
+			meta.Channel.BaseURL,
+			model,
+			apiVersion,
+		), nil
 	case mode.Completions:
-		return fmt.Sprintf("%s/openai/deployments/%s/completions?api-version=%s", meta.Channel.BaseURL, model, apiVersion), nil
+		return fmt.Sprintf(
+			"%s/openai/deployments/%s/completions?api-version=%s",
+			meta.Channel.BaseURL,
+			model,
+			apiVersion,
+		), nil
 	case mode.Embeddings:
-		return fmt.Sprintf("%s/openai/deployments/%s/embeddings?api-version=%s", meta.Channel.BaseURL, model, apiVersion), nil
+		return fmt.Sprintf(
+			"%s/openai/deployments/%s/embeddings?api-version=%s",
+			meta.Channel.BaseURL,
+			model,
+			apiVersion,
+		), nil
 	default:
 		return "", fmt.Errorf("unsupported mode: %s", meta.Mode)
 	}

+ 19 - 4
core/relay/adaptor/baidu/adaptor.go

@@ -89,7 +89,10 @@ func (a *Adaptor) SetupRequestHeader(meta *meta.Meta, _ *gin.Context, req *http.
 	return nil
 }
 
-func (a *Adaptor) ConvertRequest(meta *meta.Meta, req *http.Request) (*adaptor.ConvertRequestResult, error) {
+func (a *Adaptor) ConvertRequest(
+	meta *meta.Meta,
+	req *http.Request,
+) (*adaptor.ConvertRequestResult, error) {
 	switch meta.Mode {
 	case mode.Embeddings:
 		return openai.ConvertEmbeddingsRequest(meta, req, true)
@@ -104,11 +107,19 @@ func (a *Adaptor) ConvertRequest(meta *meta.Meta, req *http.Request) (*adaptor.C
 	}
 }
 
-func (a *Adaptor) DoRequest(_ *meta.Meta, _ *gin.Context, req *http.Request) (*http.Response, error) {
+func (a *Adaptor) DoRequest(
+	_ *meta.Meta,
+	_ *gin.Context,
+	req *http.Request,
+) (*http.Response, error) {
 	return utils.DoRequest(req)
 }
 
-func (a *Adaptor) DoResponse(meta *meta.Meta, c *gin.Context, resp *http.Response) (usage *model.Usage, err adaptor.Error) {
+func (a *Adaptor) DoResponse(
+	meta *meta.Meta,
+	c *gin.Context,
+	resp *http.Response,
+) (usage *model.Usage, err adaptor.Error) {
 	switch meta.Mode {
 	case mode.Embeddings:
 		usage, err = EmbeddingsHandler(meta, c, resp)
@@ -123,7 +134,11 @@ func (a *Adaptor) DoResponse(meta *meta.Meta, c *gin.Context, resp *http.Respons
 			usage, err = Handler(meta, c, resp)
 		}
 	default:
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(fmt.Sprintf("unsupported mode: %s", meta.Mode), nil, http.StatusBadRequest)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			fmt.Sprintf("unsupported mode: %s", meta.Mode),
+			nil,
+			http.StatusBadRequest,
+		)
 	}
 	return
 }

+ 25 - 5
core/relay/adaptor/baidu/embeddings.go

@@ -18,19 +18,31 @@ type EmbeddingsResponse struct {
 	Usage relaymodel.Usage `json:"usage"`
 }
 
-func EmbeddingsHandler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model.Usage, adaptor.Error) {
+func EmbeddingsHandler(
+	meta *meta.Meta,
+	c *gin.Context,
+	resp *http.Response,
+) (*model.Usage, adaptor.Error) {
 	defer resp.Body.Close()
 
 	log := middleware.GetLogger(c)
 
 	body, err := io.ReadAll(resp.Body)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(err.Error(), nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			err.Error(),
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 	var baiduResponse EmbeddingsResponse
 	err = sonic.Unmarshal(body, &baiduResponse)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(err.Error(), nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			err.Error(),
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 	if baiduResponse.Error != nil && baiduResponse.ErrorCode != 0 {
 		return baiduResponse.Usage.ToModelUsage(), ErrorHandler(baiduResponse.Error)
@@ -39,14 +51,22 @@ func EmbeddingsHandler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*m
 	respMap := make(map[string]any)
 	err = sonic.Unmarshal(body, &respMap)
 	if err != nil {
-		return baiduResponse.Usage.ToModelUsage(), relaymodel.WrapperOpenAIErrorWithMessage(err.Error(), nil, http.StatusInternalServerError)
+		return baiduResponse.Usage.ToModelUsage(), relaymodel.WrapperOpenAIErrorWithMessage(
+			err.Error(),
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 	respMap["model"] = meta.OriginModel
 	respMap["object"] = "list"
 
 	data, err := sonic.Marshal(respMap)
 	if err != nil {
-		return baiduResponse.Usage.ToModelUsage(), relaymodel.WrapperOpenAIErrorWithMessage(err.Error(), nil, http.StatusInternalServerError)
+		return baiduResponse.Usage.ToModelUsage(), relaymodel.WrapperOpenAIErrorWithMessage(
+			err.Error(),
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 	_, err = c.Writer.Write(data)
 	if err != nil {

+ 5 - 1
core/relay/adaptor/baidu/error.go

@@ -44,5 +44,9 @@ func ErrorHandler(baiduError *Error) adaptor.Error {
 			http.StatusTooManyRequests,
 		)
 	}
-	return relaymodel.WrapperOpenAIErrorWithMessage(baiduError.ErrorMsg, "upstream_"+strconv.Itoa(baiduError.ErrorCode), http.StatusInternalServerError)
+	return relaymodel.WrapperOpenAIErrorWithMessage(
+		baiduError.ErrorMsg,
+		"upstream_"+strconv.Itoa(baiduError.ErrorCode),
+		http.StatusInternalServerError,
+	)
 }

+ 16 - 4
core/relay/adaptor/baidu/image.go

@@ -31,12 +31,20 @@ func ImageHandler(_ *meta.Meta, c *gin.Context, resp *http.Response) (*model.Usa
 
 	body, err := io.ReadAll(resp.Body)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(err.Error(), nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			err.Error(),
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 	var imageResponse ImageResponse
 	err = sonic.Unmarshal(body, &imageResponse)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(err.Error(), nil, http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			err.Error(),
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 
 	usage := &model.Usage{
@@ -44,14 +52,18 @@ func ImageHandler(_ *meta.Meta, c *gin.Context, resp *http.Response) (*model.Usa
 		TotalTokens: model.ZeroNullInt64(len(imageResponse.Data)),
 	}
 
-	if imageResponse.Error != nil && imageResponse.Error.ErrorMsg != "" {
+	if imageResponse.Error != nil && imageResponse.ErrorMsg != "" {
 		return usage, ErrorHandler(imageResponse.Error)
 	}
 
 	openaiResponse := ToOpenAIImageResponse(&imageResponse)
 	data, err := sonic.Marshal(openaiResponse)
 	if err != nil {
-		return usage, relaymodel.WrapperOpenAIErrorWithMessage(err.Error(), nil, http.StatusInternalServerError)
+		return usage, relaymodel.WrapperOpenAIErrorWithMessage(
+			err.Error(),
+			nil,
+			http.StatusInternalServerError,
+		)
 	}
 	_, err = c.Writer.Write(data)
 	if err != nil {

+ 21 - 6
core/relay/adaptor/baidu/main.go

@@ -109,7 +109,10 @@ func response2OpenAI(meta *meta.Meta, response *ChatResponse) *relaymodel.TextRe
 	return &fullTextResponse
 }
 
-func streamResponse2OpenAI(meta *meta.Meta, baiduResponse *ChatStreamResponse) *relaymodel.ChatCompletionsStreamResponse {
+func streamResponse2OpenAI(
+	meta *meta.Meta,
+	baiduResponse *ChatStreamResponse,
+) *relaymodel.ChatCompletionsStreamResponse {
 	var choice relaymodel.ChatCompletionsStreamResponseChoice
 	choice.Delta.Content = baiduResponse.Result
 	if baiduResponse.IsEnd {
@@ -126,7 +129,11 @@ func streamResponse2OpenAI(meta *meta.Meta, baiduResponse *ChatStreamResponse) *
 	return &response
 }
 
-func StreamHandler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model.Usage, adaptor.Error) {
+func StreamHandler(
+	meta *meta.Meta,
+	c *gin.Context,
+	resp *http.Response,
+) (*model.Usage, adaptor.Error) {
 	defer resp.Body.Close()
 
 	log := middleware.GetLogger(c)
@@ -176,18 +183,26 @@ func Handler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model.Usage
 	var baiduResponse ChatResponse
 	err := sonic.ConfigDefault.NewDecoder(resp.Body).Decode(&baiduResponse)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIError(err, "unmarshal_response_body_failed", http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIError(
+			err,
+			"unmarshal_response_body_failed",
+			http.StatusInternalServerError,
+		)
 	}
-	if baiduResponse.Error != nil && baiduResponse.Error.ErrorCode != 0 {
+	if baiduResponse.Error != nil && baiduResponse.ErrorCode != 0 {
 		return nil, ErrorHandler(baiduResponse.Error)
 	}
 	fullTextResponse := response2OpenAI(meta, &baiduResponse)
 	jsonResponse, err := sonic.Marshal(fullTextResponse)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIError(err, "marshal_response_body_failed", http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIError(
+			err,
+			"marshal_response_body_failed",
+			http.StatusInternalServerError,
+		)
 	}
 	c.Writer.Header().Set("Content-Type", "application/json")
 	c.Writer.WriteHeader(resp.StatusCode)
 	_, _ = c.Writer.Write(jsonResponse)
-	return fullTextResponse.Usage.ToModelUsage(), nil
+	return fullTextResponse.ToModelUsage(), nil
 }

+ 25 - 5
core/relay/adaptor/baidu/rerank.go

@@ -18,19 +18,31 @@ type RerankResponse struct {
 	Usage relaymodel.Usage `json:"usage"`
 }
 
-func RerankHandler(_ *meta.Meta, c *gin.Context, resp *http.Response) (*model.Usage, adaptor.Error) {
+func RerankHandler(
+	_ *meta.Meta,
+	c *gin.Context,
+	resp *http.Response,
+) (*model.Usage, adaptor.Error) {
 	defer resp.Body.Close()
 
 	log := middleware.GetLogger(c)
 
 	respBody, err := io.ReadAll(resp.Body)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIError(err, "read_response_body_failed", http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIError(
+			err,
+			"read_response_body_failed",
+			http.StatusInternalServerError,
+		)
 	}
 	reRankResp := &RerankResponse{}
 	err = sonic.Unmarshal(respBody, reRankResp)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIError(err, "unmarshal_response_body_failed", http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIError(
+			err,
+			"unmarshal_response_body_failed",
+			http.StatusInternalServerError,
+		)
 	}
 	if reRankResp.Error != nil && reRankResp.Error.ErrorCode != 0 {
 		return nil, ErrorHandler(reRankResp.Error)
@@ -38,7 +50,11 @@ func RerankHandler(_ *meta.Meta, c *gin.Context, resp *http.Response) (*model.Us
 	respMap := make(map[string]any)
 	err = sonic.Unmarshal(respBody, &respMap)
 	if err != nil {
-		return reRankResp.Usage.ToModelUsage(), relaymodel.WrapperOpenAIError(err, "unmarshal_response_body_failed", http.StatusInternalServerError)
+		return reRankResp.Usage.ToModelUsage(), relaymodel.WrapperOpenAIError(
+			err,
+			"unmarshal_response_body_failed",
+			http.StatusInternalServerError,
+		)
 	}
 	delete(respMap, "model")
 	delete(respMap, "usage")
@@ -52,7 +68,11 @@ func RerankHandler(_ *meta.Meta, c *gin.Context, resp *http.Response) (*model.Us
 	delete(respMap, "results")
 	jsonData, err := sonic.Marshal(respMap)
 	if err != nil {
-		return reRankResp.Usage.ToModelUsage(), relaymodel.WrapperOpenAIError(err, "marshal_response_body_failed", http.StatusInternalServerError)
+		return reRankResp.Usage.ToModelUsage(), relaymodel.WrapperOpenAIError(
+			err,
+			"marshal_response_body_failed",
+			http.StatusInternalServerError,
+		)
 	}
 	_, err = c.Writer.Write(jsonData)
 	if err != nil {

+ 19 - 6
core/relay/adaptor/baidu/token.go

@@ -35,7 +35,11 @@ func GetAccessToken(ctx context.Context, apiKey string) (string, error) {
 		log.Errorf("get baidu access token failed: %v", err)
 		return "", errors.New("get baidu access token failed")
 	}
-	tokenCache.Set(apiKey, accessToken.AccessToken, time.Duration(accessToken.ExpiresIn)*time.Second-time.Minute*10)
+	tokenCache.Set(
+		apiKey,
+		accessToken.AccessToken,
+		time.Duration(accessToken.ExpiresIn)*time.Second-time.Minute*10,
+	)
 	return accessToken.AccessToken, nil
 }
 
@@ -44,11 +48,16 @@ func getBaiduAccessTokenHelper(ctx context.Context, apiKey string) (*AccessToken
 	if err != nil {
 		return nil, err
 	}
-	req, err := http.NewRequestWithContext(ctx,
+	req, err := http.NewRequestWithContext(
+		ctx,
 		http.MethodPost,
-		fmt.Sprintf("https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=%s&client_secret=%s",
-			clientID, clientSecret),
-		nil)
+		fmt.Sprintf(
+			"https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=%s&client_secret=%s",
+			clientID,
+			clientSecret,
+		),
+		nil,
+	)
 	if err != nil {
 		return nil, err
 	}
@@ -66,7 +75,11 @@ func getBaiduAccessTokenHelper(ctx context.Context, apiKey string) (*AccessToken
 		return nil, err
 	}
 	if accessToken.Error != "" {
-		return nil, fmt.Errorf("get baidu access token failed: %s: %s", accessToken.Error, accessToken.ErrorDescription)
+		return nil, fmt.Errorf(
+			"get baidu access token failed: %s: %s",
+			accessToken.Error,
+			accessToken.ErrorDescription,
+		)
 	}
 	if accessToken.AccessToken == "" {
 		return nil, errors.New("get baidu access token return empty access token")

+ 14 - 3
core/relay/adaptor/baiduv2/adaptor.go

@@ -59,7 +59,10 @@ func (a *Adaptor) SetupRequestHeader(meta *meta.Meta, _ *gin.Context, req *http.
 	return nil
 }
 
-func (a *Adaptor) ConvertRequest(meta *meta.Meta, req *http.Request) (*adaptor.ConvertRequestResult, error) {
+func (a *Adaptor) ConvertRequest(
+	meta *meta.Meta,
+	req *http.Request,
+) (*adaptor.ConvertRequestResult, error) {
 	switch meta.Mode {
 	case mode.ChatCompletions, mode.Rerank:
 		actModel := meta.ActualModel
@@ -74,11 +77,19 @@ func (a *Adaptor) ConvertRequest(meta *meta.Meta, req *http.Request) (*adaptor.C
 	}
 }
 
-func (a *Adaptor) DoRequest(_ *meta.Meta, _ *gin.Context, req *http.Request) (*http.Response, error) {
+func (a *Adaptor) DoRequest(
+	_ *meta.Meta,
+	_ *gin.Context,
+	req *http.Request,
+) (*http.Response, error) {
 	return utils.DoRequest(req)
 }
 
-func (a *Adaptor) DoResponse(meta *meta.Meta, c *gin.Context, resp *http.Response) (usage *model.Usage, err adaptor.Error) {
+func (a *Adaptor) DoResponse(
+	meta *meta.Meta,
+	c *gin.Context,
+	resp *http.Response,
+) (usage *model.Usage, err adaptor.Error) {
 	switch meta.Mode {
 	case mode.ChatCompletions, mode.Rerank:
 		return openai.DoResponse(meta, c, resp)

+ 17 - 3
core/relay/adaptor/baiduv2/token.go

@@ -35,7 +35,11 @@ func GetBearerToken(ctx context.Context, apiKey string) (string, error) {
 		log.Errorf("get baiduv2 access token failed: %v", err)
 		return "", errors.New("get baiduv2 access token failed")
 	}
-	tokenCache.Set(apiKey, tokenResponse.Token, time.Until(tokenResponse.ExpireTime.Add(-time.Minute*10)))
+	tokenCache.Set(
+		apiKey,
+		tokenResponse.Token,
+		time.Until(tokenResponse.ExpireTime.Add(-time.Minute*10)),
+	)
 	return tokenResponse.Token, nil
 }
 
@@ -50,7 +54,12 @@ func getBaiduAccessTokenHelper(ctx context.Context, apiKey string) (*TokenRespon
 		return nil, err
 	}
 	authorization := generateAuthorizationString(ak, sk)
-	req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://iam.bj.baidubce.com/v1/BCE-BEARER/token", nil)
+	req, err := http.NewRequestWithContext(
+		ctx,
+		http.MethodGet,
+		"https://iam.bj.baidubce.com/v1/BCE-BEARER/token",
+		nil,
+	)
 	if err != nil {
 		return nil, err
 	}
@@ -83,7 +92,12 @@ func generateAuthorizationString(ak, sk string) string {
 
 	timestamp := time.Now().UTC().Format("2006-01-02T15:04:05Z")
 	expirationPeriodInSeconds := 1800
-	authStringPrefix := fmt.Sprintf("bce-auth-v1/%s/%s/%d", ak, timestamp, expirationPeriodInSeconds)
+	authStringPrefix := fmt.Sprintf(
+		"bce-auth-v1/%s/%s/%d",
+		ak,
+		timestamp,
+		expirationPeriodInSeconds,
+	)
 
 	signingKey := hmacSHA256(sk, authStringPrefix)
 

+ 2 - 1
core/relay/adaptor/cloudflare/adaptor.go

@@ -24,7 +24,8 @@ func (a *Adaptor) GetBaseURL() string {
 // https://developers.cloudflare.com/ai-gateway/providers/workersai/#openai-compatible-endpoints
 // https://gateway.ai.cloudflare.com/v1/{account_id}/{gateway_id}/workers-ai
 func isAIGateWay(baseURL string) bool {
-	return strings.HasPrefix(baseURL, "https://gateway.ai.cloudflare.com") && strings.HasSuffix(baseURL, "/workers-ai")
+	return strings.HasPrefix(baseURL, "https://gateway.ai.cloudflare.com") &&
+		strings.HasSuffix(baseURL, "/workers-ai")
 }
 
 func (a *Adaptor) GetRequestURL(meta *meta.Meta) (string, error) {

+ 14 - 3
core/relay/adaptor/cohere/adaptor.go

@@ -32,7 +32,10 @@ func (a *Adaptor) SetupRequestHeader(meta *meta.Meta, _ *gin.Context, req *http.
 	return nil
 }
 
-func (a *Adaptor) ConvertRequest(meta *meta.Meta, req *http.Request) (*adaptor.ConvertRequestResult, error) {
+func (a *Adaptor) ConvertRequest(
+	meta *meta.Meta,
+	req *http.Request,
+) (*adaptor.ConvertRequestResult, error) {
 	request, err := utils.UnmarshalGeneralOpenAIRequest(req)
 	if err != nil {
 		return nil, err
@@ -53,11 +56,19 @@ func (a *Adaptor) ConvertRequest(meta *meta.Meta, req *http.Request) (*adaptor.C
 	}, nil
 }
 
-func (a *Adaptor) DoRequest(_ *meta.Meta, _ *gin.Context, req *http.Request) (*http.Response, error) {
+func (a *Adaptor) DoRequest(
+	_ *meta.Meta,
+	_ *gin.Context,
+	req *http.Request,
+) (*http.Response, error) {
 	return utils.DoRequest(req)
 }
 
-func (a *Adaptor) DoResponse(meta *meta.Meta, c *gin.Context, resp *http.Response) (usage *model.Usage, err adaptor.Error) {
+func (a *Adaptor) DoResponse(
+	meta *meta.Meta,
+	c *gin.Context,
+	resp *http.Response,
+) (usage *model.Usage, err adaptor.Error) {
 	switch meta.Mode {
 	case mode.Rerank:
 		usage, err = openai.RerankHandler(meta, c, resp)

+ 28 - 8
core/relay/adaptor/cohere/main.go

@@ -53,8 +53,9 @@ func ConvertRequest(textRequest *relaymodel.GeneralOpenAIRequest) *Request {
 		cohereRequest.Connectors = append(cohereRequest.Connectors, WebSearchConnector)
 	}
 	for _, message := range textRequest.Messages {
+		messageContent, _ := message.Content.(string)
 		if message.Role == "user" {
-			cohereRequest.Message = message.Content.(string)
+			cohereRequest.Message = messageContent
 		} else {
 			var role string
 			switch message.Role {
@@ -67,14 +68,17 @@ func ConvertRequest(textRequest *relaymodel.GeneralOpenAIRequest) *Request {
 			}
 			cohereRequest.ChatHistory = append(cohereRequest.ChatHistory, ChatMessage{
 				Role:    role,
-				Message: message.Content.(string),
+				Message: messageContent,
 			})
 		}
 	}
 	return &cohereRequest
 }
 
-func StreamResponse2OpenAI(meta *meta.Meta, cohereResponse *StreamResponse) *relaymodel.ChatCompletionsStreamResponse {
+func StreamResponse2OpenAI(
+	meta *meta.Meta,
+	cohereResponse *StreamResponse,
+) *relaymodel.ChatCompletionsStreamResponse {
 	var response *Response
 	var responseText string
 	var finishReason string
@@ -147,7 +151,11 @@ func Response2OpenAI(meta *meta.Meta, cohereResponse *Response) *relaymodel.Text
 	return &fullTextResponse
 }
 
-func StreamHandler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model.Usage, adaptor.Error) {
+func StreamHandler(
+	meta *meta.Meta,
+	c *gin.Context,
+	resp *http.Response,
+) (*model.Usage, adaptor.Error) {
 	if resp.StatusCode != http.StatusOK {
 		return nil, openai.ErrorHanlder(resp)
 	}
@@ -201,18 +209,30 @@ func Handler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model.Usage
 	var cohereResponse Response
 	err := sonic.ConfigDefault.NewDecoder(resp.Body).Decode(&cohereResponse)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIError(err, "unmarshal_response_body_failed", http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIError(
+			err,
+			"unmarshal_response_body_failed",
+			http.StatusInternalServerError,
+		)
 	}
 	if cohereResponse.ResponseID == "" {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(cohereResponse.Message, resp.StatusCode, resp.StatusCode)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			cohereResponse.Message,
+			resp.StatusCode,
+			resp.StatusCode,
+		)
 	}
 	fullTextResponse := Response2OpenAI(meta, &cohereResponse)
 	jsonResponse, err := sonic.Marshal(fullTextResponse)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIError(err, "marshal_response_body_failed", http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIError(
+			err,
+			"marshal_response_body_failed",
+			http.StatusInternalServerError,
+		)
 	}
 	c.Writer.Header().Set("Content-Type", "application/json")
 	c.Writer.WriteHeader(resp.StatusCode)
 	_, _ = c.Writer.Write(jsonResponse)
-	return fullTextResponse.Usage.ToModelUsage(), nil
+	return fullTextResponse.ToModelUsage(), nil
 }

+ 14 - 3
core/relay/adaptor/coze/adaptor.go

@@ -36,7 +36,10 @@ func (a *Adaptor) SetupRequestHeader(meta *meta.Meta, _ *gin.Context, req *http.
 	return nil
 }
 
-func (a *Adaptor) ConvertRequest(meta *meta.Meta, req *http.Request) (*adaptor.ConvertRequestResult, error) {
+func (a *Adaptor) ConvertRequest(
+	meta *meta.Meta,
+	req *http.Request,
+) (*adaptor.ConvertRequestResult, error) {
 	if meta.Mode != mode.ChatCompletions {
 		return nil, errors.New("coze only support chat completions")
 	}
@@ -77,11 +80,19 @@ func (a *Adaptor) ConvertRequest(meta *meta.Meta, req *http.Request) (*adaptor.C
 	}, nil
 }
 
-func (a *Adaptor) DoRequest(_ *meta.Meta, _ *gin.Context, req *http.Request) (*http.Response, error) {
+func (a *Adaptor) DoRequest(
+	_ *meta.Meta,
+	_ *gin.Context,
+	req *http.Request,
+) (*http.Response, error) {
 	return utils.DoRequest(req)
 }
 
-func (a *Adaptor) DoResponse(meta *meta.Meta, c *gin.Context, resp *http.Response) (usage *model.Usage, err adaptor.Error) {
+func (a *Adaptor) DoResponse(
+	meta *meta.Meta,
+	c *gin.Context,
+	resp *http.Response,
+) (usage *model.Usage, err adaptor.Error) {
 	if utils.IsStreamResponse(resp) {
 		usage, err = StreamHandler(meta, c, resp)
 	} else {

+ 30 - 7
core/relay/adaptor/coze/main.go

@@ -37,7 +37,10 @@ func stopReasonCoze2OpenAI(reason *string) relaymodel.FinishReason {
 	}
 }
 
-func StreamResponse2OpenAI(meta *meta.Meta, cozeResponse *StreamResponse) *relaymodel.ChatCompletionsStreamResponse {
+func StreamResponse2OpenAI(
+	meta *meta.Meta,
+	cozeResponse *StreamResponse,
+) *relaymodel.ChatCompletionsStreamResponse {
 	var stopReason string
 	var choice relaymodel.ChatCompletionsStreamResponseChoice
 
@@ -89,7 +92,11 @@ func Response2OpenAI(meta *meta.Meta, cozeResponse *Response) *relaymodel.TextRe
 	return &fullTextResponse
 }
 
-func StreamHandler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model.Usage, adaptor.Error) {
+func StreamHandler(
+	meta *meta.Meta,
+	c *gin.Context,
+	resp *http.Response,
+) (*model.Usage, adaptor.Error) {
 	if resp.StatusCode != http.StatusOK {
 		return nil, openai.ErrorHanlder(resp)
 	}
@@ -144,7 +151,9 @@ func StreamHandler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model
 
 	render.Done(c)
 
-	return openai.ResponseText2Usage(responseText.String(), meta.ActualModel, int64(meta.RequestUsage.InputTokens)).ToModelUsage(), nil
+	return openai.ResponseText2Usage(responseText.String(), meta.ActualModel, int64(meta.RequestUsage.InputTokens)).
+			ToModelUsage(),
+		nil
 }
 
 func Handler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model.Usage, adaptor.Error) {
@@ -159,15 +168,27 @@ func Handler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model.Usage
 	var cozeResponse Response
 	err := sonic.ConfigDefault.NewDecoder(resp.Body).Decode(&cozeResponse)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIError(err, "unmarshal_response_body_failed", http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIError(
+			err,
+			"unmarshal_response_body_failed",
+			http.StatusInternalServerError,
+		)
 	}
 	if cozeResponse.Code != 0 {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(cozeResponse.Msg, cozeResponse.Code, resp.StatusCode)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			cozeResponse.Msg,
+			cozeResponse.Code,
+			resp.StatusCode,
+		)
 	}
 	fullTextResponse := Response2OpenAI(meta, &cozeResponse)
 	jsonResponse, err := sonic.Marshal(fullTextResponse)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIError(err, "marshal_response_body_failed", http.StatusInternalServerError)
+		return nil, relaymodel.WrapperOpenAIError(
+			err,
+			"marshal_response_body_failed",
+			http.StatusInternalServerError,
+		)
 	}
 	c.Writer.Header().Set("Content-Type", "application/json")
 	c.Writer.WriteHeader(resp.StatusCode)
@@ -179,5 +200,7 @@ func Handler(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model.Usage
 	if len(fullTextResponse.Choices) > 0 {
 		responseText = fullTextResponse.Choices[0].Message.StringContent()
 	}
-	return openai.ResponseText2Usage(responseText, meta.ActualModel, int64(meta.RequestUsage.InputTokens)).ToModelUsage(), nil
+	return openai.ResponseText2Usage(responseText, meta.ActualModel, int64(meta.RequestUsage.InputTokens)).
+			ToModelUsage(),
+		nil
 }

+ 19 - 4
core/relay/adaptor/doc2x/adaptor.go

@@ -32,7 +32,10 @@ func (a *Adaptor) GetRequestURL(meta *meta.Meta) (string, error) {
 	}
 }
 
-func (a *Adaptor) ConvertRequest(meta *meta.Meta, req *http.Request) (*adaptor.ConvertRequestResult, error) {
+func (a *Adaptor) ConvertRequest(
+	meta *meta.Meta,
+	req *http.Request,
+) (*adaptor.ConvertRequestResult, error) {
 	switch meta.Mode {
 	case mode.ParsePdf:
 		return ConvertParsePdfRequest(meta, req)
@@ -41,16 +44,28 @@ func (a *Adaptor) ConvertRequest(meta *meta.Meta, req *http.Request) (*adaptor.C
 	}
 }
 
-func (a *Adaptor) DoRequest(_ *meta.Meta, _ *gin.Context, req *http.Request) (*http.Response, error) {
+func (a *Adaptor) DoRequest(
+	_ *meta.Meta,
+	_ *gin.Context,
+	req *http.Request,
+) (*http.Response, error) {
 	return utils.DoRequest(req)
 }
 
-func (a *Adaptor) DoResponse(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model.Usage, adaptor.Error) {
+func (a *Adaptor) DoResponse(
+	meta *meta.Meta,
+	c *gin.Context,
+	resp *http.Response,
+) (*model.Usage, adaptor.Error) {
 	switch meta.Mode {
 	case mode.ParsePdf:
 		return HandleParsePdfResponse(meta, c, resp)
 	default:
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage(fmt.Sprintf("unsupported mode: %s", meta.Mode), "unsupported_mode", http.StatusBadRequest)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			fmt.Sprintf("unsupported mode: %s", meta.Mode),
+			"unsupported_mode",
+			http.StatusBadRequest,
+		)
 	}
 }
 

+ 2 - 1
core/relay/adaptor/doc2x/html2md_test.go

@@ -51,7 +51,8 @@ func TestHTMLTable2Md(t *testing.T) {
 	}
 }
 
-// var htmlImage = `<img src="https://cdn.noedgeai.com/01956426-b164-730d-a1fe-8be8972145d6_0.jpg?x=258&y=694&w=1132&h=826"/>`
+// var htmlImage = `<img
+// src="https://cdn.noedgeai.com/01956426-b164-730d-a1fe-8be8972145d6_0.jpg?x=258&y=694&w=1132&h=826"/>`
 
 // func TestInlineMdImage(t *testing.T) {
 // 	t.Parallel()

+ 40 - 9
core/relay/adaptor/doc2x/pdf.go

@@ -23,7 +23,10 @@ import (
 	log "github.com/sirupsen/logrus"
 )
 
-func ConvertParsePdfRequest(meta *meta.Meta, req *http.Request) (*adaptor.ConvertRequestResult, error) {
+func ConvertParsePdfRequest(
+	meta *meta.Meta,
+	req *http.Request,
+) (*adaptor.ConvertRequestResult, error) {
 	err := req.ParseMultipartForm(1024 * 1024 * 4)
 	if err != nil {
 		return nil, err
@@ -54,21 +57,37 @@ type ParsePdfResponseData struct {
 	UID string `json:"uid"`
 }
 
-func HandleParsePdfResponse(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model.Usage, adaptor.Error) {
+func HandleParsePdfResponse(
+	meta *meta.Meta,
+	c *gin.Context,
+	resp *http.Response,
+) (*model.Usage, adaptor.Error) {
 	var response ParsePdfResponse
 	err := sonic.ConfigDefault.NewDecoder(resp.Body).Decode(&response)
 	if err != nil {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage("decode response failed: "+err.Error(), "decode_response_failed", http.StatusBadRequest)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			"decode response failed: "+err.Error(),
+			"decode_response_failed",
+			http.StatusBadRequest,
+		)
 	}
 
 	if response.Code != "success" {
-		return nil, relaymodel.WrapperOpenAIErrorWithMessage("parse pdf failed: "+response.Msg, "parse_pdf_failed", http.StatusBadRequest)
+		return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+			"parse pdf failed: "+response.Msg,
+			"parse_pdf_failed",
+			http.StatusBadRequest,
+		)
 	}
 
 	for {
 		status, err := GetStatus(context.Background(), meta, response.Data.UID)
 		if err != nil {
-			return nil, relaymodel.WrapperOpenAIErrorWithMessage("get status failed: "+err.Error(), "get_status_failed", http.StatusInternalServerError)
+			return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+				"get status failed: "+err.Error(),
+				"get_status_failed",
+				http.StatusInternalServerError,
+			)
 		}
 
 		switch status.Status {
@@ -77,7 +96,11 @@ func HandleParsePdfResponse(meta *meta.Meta, c *gin.Context, resp *http.Response
 		case StatusResponseDataStatusProcessing:
 			time.Sleep(1 * time.Second)
 		case StatusResponseDataStatusFailed:
-			return nil, relaymodel.WrapperOpenAIErrorWithMessage("parse pdf failed: "+status.Detail, "parse_pdf_failed", http.StatusBadRequest)
+			return nil, relaymodel.WrapperOpenAIErrorWithMessage(
+				"parse pdf failed: "+status.Detail,
+				"parse_pdf_failed",
+				http.StatusBadRequest,
+			)
 		}
 	}
 }
@@ -245,7 +268,7 @@ func InlineMdImage(ctx context.Context, text string) string {
 	return resultText.String()
 }
 
-func imageURL2MdBase64(ctx context.Context, url string, altText string) (string, error) {
+func imageURL2MdBase64(ctx context.Context, url, altText string) (string, error) {
 	req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
 	if err != nil {
 		return "", fmt.Errorf("failed to create request: %w", err)
@@ -264,7 +287,11 @@ func imageURL2MdBase64(ctx context.Context, url string, altText string) (string,
 		if resp.StatusCode == http.StatusNotFound {
 			resp.Body.Close()
 			if retries == maxRetries {
-				return "", fmt.Errorf("failed to download image, status code: %d after %d retries", resp.StatusCode, retries)
+				return "", fmt.Errorf(
+					"failed to download image, status code: %d after %d retries",
+					resp.StatusCode,
+					retries,
+				)
 			}
 			retries++
 			time.Sleep(1 * time.Second)
@@ -321,7 +348,11 @@ func handleConvertPdfToMd(ctx context.Context, str string) string {
 	return result
 }
 
-func handleParsePdfResponse(meta *meta.Meta, c *gin.Context, response *StatusResponseDataResult) (*model.Usage, adaptor.Error) {
+func handleParsePdfResponse(
+	meta *meta.Meta,
+	c *gin.Context,
+	response *StatusResponseDataResult,
+) (*model.Usage, adaptor.Error) {
 	mds := make([]string, 0, len(response.Pages))
 	totalLength := 0
 	for _, page := range response.Pages {

+ 9 - 2
core/relay/adaptor/doubao/main.go

@@ -52,7 +52,10 @@ func (a *Adaptor) GetRequestURL(meta *meta.Meta) (string, error) {
 	return GetRequestURL(meta)
 }
 
-func (a *Adaptor) ConvertRequest(meta *meta.Meta, req *http.Request) (*adaptor.ConvertRequestResult, error) {
+func (a *Adaptor) ConvertRequest(
+	meta *meta.Meta,
+	req *http.Request,
+) (*adaptor.ConvertRequestResult, error) {
 	result, err := a.Adaptor.ConvertRequest(meta, req)
 	if err != nil {
 		return nil, err
@@ -133,7 +136,11 @@ func handlerPreHandler(meta *meta.Meta, node *ast.Node, websearchCount *int64) e
 	})
 }
 
-func (a *Adaptor) DoResponse(meta *meta.Meta, c *gin.Context, resp *http.Response) (usage *model.Usage, err adaptor.Error) {
+func (a *Adaptor) DoResponse(
+	meta *meta.Meta,
+	c *gin.Context,
+	resp *http.Response,
+) (usage *model.Usage, err adaptor.Error) {
 	switch meta.Mode {
 	case mode.ChatCompletions:
 		websearchCount := int64(0)

+ 14 - 3
core/relay/adaptor/doubaoaudio/main.go

@@ -38,7 +38,10 @@ func (a *Adaptor) GetRequestURL(meta *meta.Meta) (string, error) {
 	return GetRequestURL(meta)
 }
 
-func (a *Adaptor) ConvertRequest(meta *meta.Meta, req *http.Request) (*adaptor.ConvertRequestResult, error) {
+func (a *Adaptor) ConvertRequest(
+	meta *meta.Meta,
+	req *http.Request,
+) (*adaptor.ConvertRequestResult, error) {
 	switch meta.Mode {
 	case mode.AudioSpeech:
 		return ConvertTTSRequest(meta, req)
@@ -61,7 +64,11 @@ func (a *Adaptor) SetupRequestHeader(meta *meta.Meta, _ *gin.Context, req *http.
 	}
 }
 
-func (a *Adaptor) DoRequest(meta *meta.Meta, _ *gin.Context, req *http.Request) (*http.Response, error) {
+func (a *Adaptor) DoRequest(
+	meta *meta.Meta,
+	_ *gin.Context,
+	req *http.Request,
+) (*http.Response, error) {
 	switch meta.Mode {
 	case mode.AudioSpeech:
 		return TTSDoRequest(meta, req)
@@ -70,7 +77,11 @@ func (a *Adaptor) DoRequest(meta *meta.Meta, _ *gin.Context, req *http.Request)
 	}
 }
 
-func (a *Adaptor) DoResponse(meta *meta.Meta, c *gin.Context, resp *http.Response) (*model.Usage, adaptor.Error) {
+func (a *Adaptor) DoResponse(
+	meta *meta.Meta,
+	c *gin.Context,
+	resp *http.Response,
+) (*model.Usage, adaptor.Error) {
 	switch meta.Mode {
 	case mode.AudioSpeech:
 		return TTSDoResponse(meta, c, resp)

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff