Bladeren bron

feat: hide group model plugin info (#280)

* feat: hide group model plugin info

* feat: add web search options enable
zijiren 6 maanden geleden
bovenliggende
commit
337e4ad5e2

+ 62 - 4
core/controller/dashboard.go

@@ -1,6 +1,7 @@
 package controller
 
 import (
+	"encoding/json"
 	"errors"
 	"fmt"
 	"net/http"
@@ -8,11 +9,13 @@ import (
 	"strconv"
 	"time"
 
+	"github.com/bytedance/sonic"
 	"github.com/gin-gonic/gin"
 	"github.com/labring/aiproxy/core/common/reqlimit"
 	"github.com/labring/aiproxy/core/controller/utils"
 	"github.com/labring/aiproxy/core/middleware"
 	"github.com/labring/aiproxy/core/model"
+	"github.com/labring/aiproxy/core/relay/mode"
 	"gorm.io/gorm"
 )
 
@@ -279,6 +282,59 @@ func GetGroupDashboard(c *gin.Context) {
 	middleware.SuccessResponse(c, dashboards)
 }
 
+type GroupModel struct {
+	CreatedAt int64                        `json:"created_at,omitempty"`
+	UpdatedAt int64                        `json:"updated_at,omitempty"`
+	Config    map[model.ModelConfigKey]any `json:"config,omitempty"`
+	Model     string                       `json:"model"`
+	Owner     model.ModelOwner             `json:"owner"`
+	Type      mode.Mode                    `json:"type"`
+	RPM       int64                        `json:"rpm,omitempty"`
+	TPM       int64                        `json:"tpm,omitempty"`
+	// map[size]map[quality]price_per_image
+	ImageQualityPrices map[string]map[string]float64 `json:"image_quality_prices,omitempty"`
+	// map[size]price_per_image
+	ImagePrices    map[string]float64 `json:"image_prices,omitempty"`
+	Price          model.Price        `json:"price,omitempty"`
+	EnabledPlugins []string           `json:"enabled_plugins,omitempty"`
+}
+
+func getEnabledPlugins(plugin map[string]json.RawMessage) []string {
+	enabledPlugins := make([]string, 0, len(plugin))
+	for pluginName, pluginConfig := range plugin {
+		pluginConfigNode, err := sonic.Get(pluginConfig)
+		if err != nil {
+			continue
+		}
+		if enable, err := pluginConfigNode.Get("enable").Bool(); err == nil && enable {
+			enabledPlugins = append(enabledPlugins, pluginName)
+		}
+	}
+	return enabledPlugins
+}
+
+func NewGroupModel(mc model.ModelConfig) GroupModel {
+	gm := GroupModel{
+		Config:             mc.Config,
+		Model:              mc.Model,
+		Owner:              mc.Owner,
+		Type:               mc.Type,
+		RPM:                mc.RPM,
+		TPM:                mc.TPM,
+		ImageQualityPrices: mc.ImageQualityPrices,
+		ImagePrices:        mc.ImagePrices,
+		Price:              mc.Price,
+		EnabledPlugins:     getEnabledPlugins(mc.Plugin),
+	}
+	if !mc.CreatedAt.IsZero() {
+		gm.CreatedAt = mc.CreatedAt.Unix()
+	}
+	if !mc.UpdatedAt.IsZero() {
+		gm.UpdatedAt = mc.UpdatedAt.Unix()
+	}
+	return gm
+}
+
 // GetGroupDashboardModels godoc
 //
 //	@Summary		Get model usage data for a specific group
@@ -287,7 +343,7 @@ func GetGroupDashboard(c *gin.Context) {
 //	@Produce		json
 //	@Security		ApiKeyAuth
 //	@Param			group	path		string	true	"Group"
-//	@Success		200		{object}	middleware.APIResponse{data=[]model.ModelConfig}
+//	@Success		200		{object}	middleware.APIResponse{data=[]GroupModel}
 //	@Router			/api/dashboard/{group}/models [get]
 func GetGroupDashboardModels(c *gin.Context) {
 	group := c.Param("group")
@@ -310,17 +366,19 @@ func GetGroupDashboardModels(c *gin.Context) {
 
 	availableSet := groupCache.GetAvailableSets()
 	enabledModelConfigs := model.LoadModelCaches().EnabledModelConfigsBySet
-	newEnabledModelConfigs := make([]model.ModelConfig, 0)
+	newEnabledModelConfigs := make([]GroupModel, 0)
 	for _, set := range availableSet {
 		for _, mc := range enabledModelConfigs[set] {
-			if slices.ContainsFunc(newEnabledModelConfigs, func(m model.ModelConfig) bool {
+			if slices.ContainsFunc(newEnabledModelConfigs, func(m GroupModel) bool {
 				return m.Model == mc.Model
 			}) {
 				continue
 			}
 			newEnabledModelConfigs = append(
 				newEnabledModelConfigs,
-				middleware.GetGroupAdjustedModelConfig(*groupCache, mc),
+				NewGroupModel(
+					middleware.GetGroupAdjustedModelConfig(*groupCache, mc),
+				),
 			)
 		}
 	}

+ 3 - 3
core/controller/mcp/publicmcp.go

@@ -110,7 +110,7 @@ func getLocalMCPTypes() []model.PublicMCPType {
 //	@Param			keyword		query		string	false	"Search keyword"
 //	@Param			status		query		int		false	"MCP status"
 //	@Success		200			{object}	middleware.APIResponse{data=[]PublicMCPResponse}
-//	@Router			/api/mcp/public/ [get]
+//	@Router			/api/mcp/publics/ [get]
 func GetPublicMCPs(c *gin.Context) {
 	page, perPage := utils.ParsePageParams(c)
 	mcpType := c.Query("type")
@@ -154,7 +154,7 @@ func GetPublicMCPs(c *gin.Context) {
 //	@Security		ApiKeyAuth
 //	@Param			status	query		int	false	"MCP status"
 //	@Success		200		{object}	middleware.APIResponse{data=[]PublicMCPResponse}
-//	@Router			/api/mcp/public/all [get]
+//	@Router			/api/mcp/publics/all [get]
 func GetAllPublicMCPs(c *gin.Context) {
 	status, _ := strconv.Atoi(c.Query("status"))
 	mcps, err := model.GetAllPublicMCPs(model.PublicMCPStatus(status))
@@ -268,7 +268,7 @@ func SavePublicMCP(c *gin.Context) {
 //	@Security		ApiKeyAuth
 //	@Param			mcp	body		[]model.PublicMCP	true	"MCP object"
 //	@Success		200	{object}	middleware.APIResponse
-//	@Router			/api/mcp/public/ [put]
+//	@Router			/api/mcp/publics/ [post]
 func SavePublicMCPs(c *gin.Context) {
 	var mcps []SavePublicMCPRequest
 	if err := c.ShouldBindJSON(&mcps); err != nil {

+ 247 - 169
core/docs/docs.go

@@ -1116,7 +1116,7 @@ const docTemplate = `{
                                         "data": {
                                             "type": "array",
                                             "items": {
-                                                "$ref": "#/definitions/model.ModelConfig"
+                                                "$ref": "#/definitions/controller.GroupModel"
                                             }
                                         }
                                     }
@@ -3683,122 +3683,6 @@ const docTemplate = `{
             }
         },
         "/api/mcp/public/": {
-            "get": {
-                "security": [
-                    {
-                        "ApiKeyAuth": []
-                    }
-                ],
-                "description": "Get a list of MCPs with pagination and filtering",
-                "produces": [
-                    "application/json"
-                ],
-                "tags": [
-                    "mcp"
-                ],
-                "summary": "Get MCPs",
-                "parameters": [
-                    {
-                        "type": "integer",
-                        "description": "Page number",
-                        "name": "page",
-                        "in": "query"
-                    },
-                    {
-                        "type": "integer",
-                        "description": "Items per page",
-                        "name": "per_page",
-                        "in": "query"
-                    },
-                    {
-                        "type": "string",
-                        "description": "hosted or local",
-                        "name": "type",
-                        "in": "query"
-                    },
-                    {
-                        "type": "string",
-                        "description": "MCP id",
-                        "name": "id",
-                        "in": "query"
-                    },
-                    {
-                        "type": "string",
-                        "description": "Search keyword",
-                        "name": "keyword",
-                        "in": "query"
-                    },
-                    {
-                        "type": "integer",
-                        "description": "MCP status",
-                        "name": "status",
-                        "in": "query"
-                    }
-                ],
-                "responses": {
-                    "200": {
-                        "description": "OK",
-                        "schema": {
-                            "allOf": [
-                                {
-                                    "$ref": "#/definitions/middleware.APIResponse"
-                                },
-                                {
-                                    "type": "object",
-                                    "properties": {
-                                        "data": {
-                                            "type": "array",
-                                            "items": {
-                                                "$ref": "#/definitions/controller.PublicMCPResponse"
-                                            }
-                                        }
-                                    }
-                                }
-                            ]
-                        }
-                    }
-                }
-            },
-            "put": {
-                "security": [
-                    {
-                        "ApiKeyAuth": []
-                    }
-                ],
-                "description": "Save a list of MCPs",
-                "consumes": [
-                    "application/json"
-                ],
-                "produces": [
-                    "application/json"
-                ],
-                "tags": [
-                    "mcp"
-                ],
-                "summary": "Save MCPs",
-                "parameters": [
-                    {
-                        "description": "MCP object",
-                        "name": "mcp",
-                        "in": "body",
-                        "required": true,
-                        "schema": {
-                            "type": "array",
-                            "items": {
-                                "$ref": "#/definitions/model.PublicMCP"
-                            }
-                        }
-                    }
-                ],
-                "responses": {
-                    "200": {
-                        "description": "OK",
-                        "schema": {
-                            "$ref": "#/definitions/middleware.APIResponse"
-                        }
-                    }
-                }
-            },
             "post": {
                 "security": [
                     {
@@ -3849,54 +3733,6 @@ const docTemplate = `{
                 }
             }
         },
-        "/api/mcp/public/all": {
-            "get": {
-                "security": [
-                    {
-                        "ApiKeyAuth": []
-                    }
-                ],
-                "description": "Get all MCPs with filtering",
-                "produces": [
-                    "application/json"
-                ],
-                "tags": [
-                    "mcp"
-                ],
-                "summary": "Get all MCPs",
-                "parameters": [
-                    {
-                        "type": "integer",
-                        "description": "MCP status",
-                        "name": "status",
-                        "in": "query"
-                    }
-                ],
-                "responses": {
-                    "200": {
-                        "description": "OK",
-                        "schema": {
-                            "allOf": [
-                                {
-                                    "$ref": "#/definitions/middleware.APIResponse"
-                                },
-                                {
-                                    "type": "object",
-                                    "properties": {
-                                        "data": {
-                                            "type": "array",
-                                            "items": {
-                                                "$ref": "#/definitions/controller.PublicMCPResponse"
-                                            }
-                                        }
-                                    }
-                                }
-                            ]
-                        }
-                    }
-                }
-            }
-        },
         "/api/mcp/public/{id}": {
             "get": {
                 "security": [
@@ -4237,6 +4073,172 @@ const docTemplate = `{
                 }
             }
         },
+        "/api/mcp/publics/": {
+            "get": {
+                "security": [
+                    {
+                        "ApiKeyAuth": []
+                    }
+                ],
+                "description": "Get a list of MCPs with pagination and filtering",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "mcp"
+                ],
+                "summary": "Get MCPs",
+                "parameters": [
+                    {
+                        "type": "integer",
+                        "description": "Page number",
+                        "name": "page",
+                        "in": "query"
+                    },
+                    {
+                        "type": "integer",
+                        "description": "Items per page",
+                        "name": "per_page",
+                        "in": "query"
+                    },
+                    {
+                        "type": "string",
+                        "description": "hosted or local",
+                        "name": "type",
+                        "in": "query"
+                    },
+                    {
+                        "type": "string",
+                        "description": "MCP id",
+                        "name": "id",
+                        "in": "query"
+                    },
+                    {
+                        "type": "string",
+                        "description": "Search keyword",
+                        "name": "keyword",
+                        "in": "query"
+                    },
+                    {
+                        "type": "integer",
+                        "description": "MCP status",
+                        "name": "status",
+                        "in": "query"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/middleware.APIResponse"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "data": {
+                                            "type": "array",
+                                            "items": {
+                                                "$ref": "#/definitions/controller.PublicMCPResponse"
+                                            }
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    }
+                }
+            },
+            "post": {
+                "security": [
+                    {
+                        "ApiKeyAuth": []
+                    }
+                ],
+                "description": "Save a list of MCPs",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "mcp"
+                ],
+                "summary": "Save MCPs",
+                "parameters": [
+                    {
+                        "description": "MCP object",
+                        "name": "mcp",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "type": "array",
+                            "items": {
+                                "$ref": "#/definitions/model.PublicMCP"
+                            }
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/middleware.APIResponse"
+                        }
+                    }
+                }
+            }
+        },
+        "/api/mcp/publics/all": {
+            "get": {
+                "security": [
+                    {
+                        "ApiKeyAuth": []
+                    }
+                ],
+                "description": "Get all MCPs with filtering",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "mcp"
+                ],
+                "summary": "Get all MCPs",
+                "parameters": [
+                    {
+                        "type": "integer",
+                        "description": "MCP status",
+                        "name": "status",
+                        "in": "query"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/middleware.APIResponse"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "data": {
+                                            "type": "array",
+                                            "items": {
+                                                "$ref": "#/definitions/controller.PublicMCPResponse"
+                                            }
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    }
+                }
+            }
+        },
         "/api/model_config/": {
             "post": {
                 "security": [
@@ -8470,6 +8472,62 @@ const docTemplate = `{
                 }
             }
         },
+        "controller.GroupModel": {
+            "type": "object",
+            "properties": {
+                "config": {
+                    "type": "object",
+                    "additionalProperties": {}
+                },
+                "created_at": {
+                    "type": "integer"
+                },
+                "enabled_plugins": {
+                    "type": "array",
+                    "items": {
+                        "type": "string"
+                    }
+                },
+                "image_prices": {
+                    "description": "map[size]price_per_image",
+                    "type": "object",
+                    "additionalProperties": {
+                        "type": "number"
+                    }
+                },
+                "image_quality_prices": {
+                    "description": "map[size]map[quality]price_per_image",
+                    "type": "object",
+                    "additionalProperties": {
+                        "type": "object",
+                        "additionalProperties": {
+                            "type": "number"
+                        }
+                    }
+                },
+                "model": {
+                    "type": "string"
+                },
+                "owner": {
+                    "$ref": "#/definitions/model.ModelOwner"
+                },
+                "price": {
+                    "$ref": "#/definitions/model.Price"
+                },
+                "rpm": {
+                    "type": "integer"
+                },
+                "tpm": {
+                    "type": "integer"
+                },
+                "type": {
+                    "$ref": "#/definitions/mode.Mode"
+                },
+                "updated_at": {
+                    "type": "integer"
+                }
+            }
+        },
         "controller.GroupPublicMCPDetailResponse": {
             "type": "object",
             "properties": {
@@ -8549,7 +8607,12 @@ const docTemplate = `{
                     }
                 },
                 "test_config": {
-                    "$ref": "#/definitions/model.TestConfig"
+                    "description": "only used by list tools",
+                    "allOf": [
+                        {
+                            "$ref": "#/definitions/model.TestConfig"
+                        }
+                    ]
                 },
                 "tools": {
                     "type": "array",
@@ -8629,7 +8692,12 @@ const docTemplate = `{
                     }
                 },
                 "test_config": {
-                    "$ref": "#/definitions/model.TestConfig"
+                    "description": "only used by list tools",
+                    "allOf": [
+                        {
+                            "$ref": "#/definitions/model.TestConfig"
+                        }
+                    ]
                 },
                 "type": {
                     "$ref": "#/definitions/model.PublicMCPType"
@@ -8836,7 +8904,12 @@ const docTemplate = `{
                     }
                 },
                 "test_config": {
-                    "$ref": "#/definitions/model.TestConfig"
+                    "description": "only used by list tools",
+                    "allOf": [
+                        {
+                            "$ref": "#/definitions/model.TestConfig"
+                        }
+                    ]
                 },
                 "type": {
                     "$ref": "#/definitions/model.PublicMCPType"
@@ -10748,7 +10821,12 @@ const docTemplate = `{
                     }
                 },
                 "test_config": {
-                    "$ref": "#/definitions/model.TestConfig"
+                    "description": "only used by list tools",
+                    "allOf": [
+                        {
+                            "$ref": "#/definitions/model.TestConfig"
+                        }
+                    ]
                 },
                 "type": {
                     "$ref": "#/definitions/model.PublicMCPType"

+ 247 - 169
core/docs/swagger.json

@@ -1107,7 +1107,7 @@
                                         "data": {
                                             "type": "array",
                                             "items": {
-                                                "$ref": "#/definitions/model.ModelConfig"
+                                                "$ref": "#/definitions/controller.GroupModel"
                                             }
                                         }
                                     }
@@ -3674,122 +3674,6 @@
             }
         },
         "/api/mcp/public/": {
-            "get": {
-                "security": [
-                    {
-                        "ApiKeyAuth": []
-                    }
-                ],
-                "description": "Get a list of MCPs with pagination and filtering",
-                "produces": [
-                    "application/json"
-                ],
-                "tags": [
-                    "mcp"
-                ],
-                "summary": "Get MCPs",
-                "parameters": [
-                    {
-                        "type": "integer",
-                        "description": "Page number",
-                        "name": "page",
-                        "in": "query"
-                    },
-                    {
-                        "type": "integer",
-                        "description": "Items per page",
-                        "name": "per_page",
-                        "in": "query"
-                    },
-                    {
-                        "type": "string",
-                        "description": "hosted or local",
-                        "name": "type",
-                        "in": "query"
-                    },
-                    {
-                        "type": "string",
-                        "description": "MCP id",
-                        "name": "id",
-                        "in": "query"
-                    },
-                    {
-                        "type": "string",
-                        "description": "Search keyword",
-                        "name": "keyword",
-                        "in": "query"
-                    },
-                    {
-                        "type": "integer",
-                        "description": "MCP status",
-                        "name": "status",
-                        "in": "query"
-                    }
-                ],
-                "responses": {
-                    "200": {
-                        "description": "OK",
-                        "schema": {
-                            "allOf": [
-                                {
-                                    "$ref": "#/definitions/middleware.APIResponse"
-                                },
-                                {
-                                    "type": "object",
-                                    "properties": {
-                                        "data": {
-                                            "type": "array",
-                                            "items": {
-                                                "$ref": "#/definitions/controller.PublicMCPResponse"
-                                            }
-                                        }
-                                    }
-                                }
-                            ]
-                        }
-                    }
-                }
-            },
-            "put": {
-                "security": [
-                    {
-                        "ApiKeyAuth": []
-                    }
-                ],
-                "description": "Save a list of MCPs",
-                "consumes": [
-                    "application/json"
-                ],
-                "produces": [
-                    "application/json"
-                ],
-                "tags": [
-                    "mcp"
-                ],
-                "summary": "Save MCPs",
-                "parameters": [
-                    {
-                        "description": "MCP object",
-                        "name": "mcp",
-                        "in": "body",
-                        "required": true,
-                        "schema": {
-                            "type": "array",
-                            "items": {
-                                "$ref": "#/definitions/model.PublicMCP"
-                            }
-                        }
-                    }
-                ],
-                "responses": {
-                    "200": {
-                        "description": "OK",
-                        "schema": {
-                            "$ref": "#/definitions/middleware.APIResponse"
-                        }
-                    }
-                }
-            },
             "post": {
                 "security": [
                     {
@@ -3840,54 +3724,6 @@
                 }
             }
         },
-        "/api/mcp/public/all": {
-            "get": {
-                "security": [
-                    {
-                        "ApiKeyAuth": []
-                    }
-                ],
-                "description": "Get all MCPs with filtering",
-                "produces": [
-                    "application/json"
-                ],
-                "tags": [
-                    "mcp"
-                ],
-                "summary": "Get all MCPs",
-                "parameters": [
-                    {
-                        "type": "integer",
-                        "description": "MCP status",
-                        "name": "status",
-                        "in": "query"
-                    }
-                ],
-                "responses": {
-                    "200": {
-                        "description": "OK",
-                        "schema": {
-                            "allOf": [
-                                {
-                                    "$ref": "#/definitions/middleware.APIResponse"
-                                },
-                                {
-                                    "type": "object",
-                                    "properties": {
-                                        "data": {
-                                            "type": "array",
-                                            "items": {
-                                                "$ref": "#/definitions/controller.PublicMCPResponse"
-                                            }
-                                        }
-                                    }
-                                }
-                            ]
-                        }
-                    }
-                }
-            }
-        },
         "/api/mcp/public/{id}": {
             "get": {
                 "security": [
@@ -4228,6 +4064,172 @@
                 }
             }
         },
+        "/api/mcp/publics/": {
+            "get": {
+                "security": [
+                    {
+                        "ApiKeyAuth": []
+                    }
+                ],
+                "description": "Get a list of MCPs with pagination and filtering",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "mcp"
+                ],
+                "summary": "Get MCPs",
+                "parameters": [
+                    {
+                        "type": "integer",
+                        "description": "Page number",
+                        "name": "page",
+                        "in": "query"
+                    },
+                    {
+                        "type": "integer",
+                        "description": "Items per page",
+                        "name": "per_page",
+                        "in": "query"
+                    },
+                    {
+                        "type": "string",
+                        "description": "hosted or local",
+                        "name": "type",
+                        "in": "query"
+                    },
+                    {
+                        "type": "string",
+                        "description": "MCP id",
+                        "name": "id",
+                        "in": "query"
+                    },
+                    {
+                        "type": "string",
+                        "description": "Search keyword",
+                        "name": "keyword",
+                        "in": "query"
+                    },
+                    {
+                        "type": "integer",
+                        "description": "MCP status",
+                        "name": "status",
+                        "in": "query"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/middleware.APIResponse"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "data": {
+                                            "type": "array",
+                                            "items": {
+                                                "$ref": "#/definitions/controller.PublicMCPResponse"
+                                            }
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    }
+                }
+            },
+            "post": {
+                "security": [
+                    {
+                        "ApiKeyAuth": []
+                    }
+                ],
+                "description": "Save a list of MCPs",
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "mcp"
+                ],
+                "summary": "Save MCPs",
+                "parameters": [
+                    {
+                        "description": "MCP object",
+                        "name": "mcp",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "type": "array",
+                            "items": {
+                                "$ref": "#/definitions/model.PublicMCP"
+                            }
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "$ref": "#/definitions/middleware.APIResponse"
+                        }
+                    }
+                }
+            }
+        },
+        "/api/mcp/publics/all": {
+            "get": {
+                "security": [
+                    {
+                        "ApiKeyAuth": []
+                    }
+                ],
+                "description": "Get all MCPs with filtering",
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "mcp"
+                ],
+                "summary": "Get all MCPs",
+                "parameters": [
+                    {
+                        "type": "integer",
+                        "description": "MCP status",
+                        "name": "status",
+                        "in": "query"
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "OK",
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/middleware.APIResponse"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "data": {
+                                            "type": "array",
+                                            "items": {
+                                                "$ref": "#/definitions/controller.PublicMCPResponse"
+                                            }
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    }
+                }
+            }
+        },
         "/api/model_config/": {
             "post": {
                 "security": [
@@ -8461,6 +8463,62 @@
                 }
             }
         },
+        "controller.GroupModel": {
+            "type": "object",
+            "properties": {
+                "config": {
+                    "type": "object",
+                    "additionalProperties": {}
+                },
+                "created_at": {
+                    "type": "integer"
+                },
+                "enabled_plugins": {
+                    "type": "array",
+                    "items": {
+                        "type": "string"
+                    }
+                },
+                "image_prices": {
+                    "description": "map[size]price_per_image",
+                    "type": "object",
+                    "additionalProperties": {
+                        "type": "number"
+                    }
+                },
+                "image_quality_prices": {
+                    "description": "map[size]map[quality]price_per_image",
+                    "type": "object",
+                    "additionalProperties": {
+                        "type": "object",
+                        "additionalProperties": {
+                            "type": "number"
+                        }
+                    }
+                },
+                "model": {
+                    "type": "string"
+                },
+                "owner": {
+                    "$ref": "#/definitions/model.ModelOwner"
+                },
+                "price": {
+                    "$ref": "#/definitions/model.Price"
+                },
+                "rpm": {
+                    "type": "integer"
+                },
+                "tpm": {
+                    "type": "integer"
+                },
+                "type": {
+                    "$ref": "#/definitions/mode.Mode"
+                },
+                "updated_at": {
+                    "type": "integer"
+                }
+            }
+        },
         "controller.GroupPublicMCPDetailResponse": {
             "type": "object",
             "properties": {
@@ -8540,7 +8598,12 @@
                     }
                 },
                 "test_config": {
-                    "$ref": "#/definitions/model.TestConfig"
+                    "description": "only used by list tools",
+                    "allOf": [
+                        {
+                            "$ref": "#/definitions/model.TestConfig"
+                        }
+                    ]
                 },
                 "tools": {
                     "type": "array",
@@ -8620,7 +8683,12 @@
                     }
                 },
                 "test_config": {
-                    "$ref": "#/definitions/model.TestConfig"
+                    "description": "only used by list tools",
+                    "allOf": [
+                        {
+                            "$ref": "#/definitions/model.TestConfig"
+                        }
+                    ]
                 },
                 "type": {
                     "$ref": "#/definitions/model.PublicMCPType"
@@ -8827,7 +8895,12 @@
                     }
                 },
                 "test_config": {
-                    "$ref": "#/definitions/model.TestConfig"
+                    "description": "only used by list tools",
+                    "allOf": [
+                        {
+                            "$ref": "#/definitions/model.TestConfig"
+                        }
+                    ]
                 },
                 "type": {
                     "$ref": "#/definitions/model.PublicMCPType"
@@ -10739,7 +10812,12 @@
                     }
                 },
                 "test_config": {
-                    "$ref": "#/definitions/model.TestConfig"
+                    "description": "only used by list tools",
+                    "allOf": [
+                        {
+                            "$ref": "#/definitions/model.TestConfig"
+                        }
+                    ]
                 },
                 "type": {
                     "$ref": "#/definitions/model.PublicMCPType"

+ 124 - 77
core/docs/swagger.yaml

@@ -238,6 +238,44 @@ definitions:
       update_at:
         type: string
     type: object
+  controller.GroupModel:
+    properties:
+      config:
+        additionalProperties: {}
+        type: object
+      created_at:
+        type: integer
+      enabled_plugins:
+        items:
+          type: string
+        type: array
+      image_prices:
+        additionalProperties:
+          type: number
+        description: map[size]price_per_image
+        type: object
+      image_quality_prices:
+        additionalProperties:
+          additionalProperties:
+            type: number
+          type: object
+        description: map[size]map[quality]price_per_image
+        type: object
+      model:
+        type: string
+      owner:
+        $ref: '#/definitions/model.ModelOwner'
+      price:
+        $ref: '#/definitions/model.Price'
+      rpm:
+        type: integer
+      tpm:
+        type: integer
+      type:
+        $ref: '#/definitions/mode.Mode'
+      updated_at:
+        type: integer
+    type: object
   controller.GroupPublicMCPDetailResponse:
     properties:
       created_at:
@@ -291,7 +329,9 @@ definitions:
           type: string
         type: array
       test_config:
-        $ref: '#/definitions/model.TestConfig'
+        allOf:
+        - $ref: '#/definitions/model.TestConfig'
+        description: only used by list tools
       tools:
         items:
           $ref: '#/definitions/mcp.Tool'
@@ -344,7 +384,9 @@ definitions:
           type: string
         type: array
       test_config:
-        $ref: '#/definitions/model.TestConfig'
+        allOf:
+        - $ref: '#/definitions/model.TestConfig'
+        description: only used by list tools
       type:
         $ref: '#/definitions/model.PublicMCPType'
       update_at:
@@ -480,7 +522,9 @@ definitions:
           type: string
         type: array
       test_config:
-        $ref: '#/definitions/model.TestConfig'
+        allOf:
+        - $ref: '#/definitions/model.TestConfig'
+        description: only used by list tools
       type:
         $ref: '#/definitions/model.PublicMCPType'
       update_at:
@@ -1808,7 +1852,9 @@ definitions:
           type: string
         type: array
       test_config:
-        $ref: '#/definitions/model.TestConfig'
+        allOf:
+        - $ref: '#/definitions/model.TestConfig'
+        description: only used by list tools
       type:
         $ref: '#/definitions/model.PublicMCPType'
       update_at:
@@ -2840,7 +2886,7 @@ paths:
             - properties:
                 data:
                   items:
-                    $ref: '#/definitions/model.ModelConfig'
+                    $ref: '#/definitions/controller.GroupModel'
                   type: array
               type: object
       security:
@@ -4409,52 +4455,6 @@ paths:
       tags:
       - mcp
   /api/mcp/public/:
-    get:
-      description: Get a list of MCPs with pagination and filtering
-      parameters:
-      - description: Page number
-        in: query
-        name: page
-        type: integer
-      - description: Items per page
-        in: query
-        name: per_page
-        type: integer
-      - description: hosted or local
-        in: query
-        name: type
-        type: string
-      - description: MCP id
-        in: query
-        name: id
-        type: string
-      - description: Search keyword
-        in: query
-        name: keyword
-        type: string
-      - description: MCP status
-        in: query
-        name: status
-        type: integer
-      produces:
-      - application/json
-      responses:
-        "200":
-          description: OK
-          schema:
-            allOf:
-            - $ref: '#/definitions/middleware.APIResponse'
-            - properties:
-                data:
-                  items:
-                    $ref: '#/definitions/controller.PublicMCPResponse'
-                  type: array
-              type: object
-      security:
-      - ApiKeyAuth: []
-      summary: Get MCPs
-      tags:
-      - mcp
     post:
       consumes:
       - application/json
@@ -4483,31 +4483,6 @@ paths:
       summary: Create MCP
       tags:
       - mcp
-    put:
-      consumes:
-      - application/json
-      description: Save a list of MCPs
-      parameters:
-      - description: MCP object
-        in: body
-        name: mcp
-        required: true
-        schema:
-          items:
-            $ref: '#/definitions/model.PublicMCP'
-          type: array
-      produces:
-      - application/json
-      responses:
-        "200":
-          description: OK
-          schema:
-            $ref: '#/definitions/middleware.APIResponse'
-      security:
-      - ApiKeyAuth: []
-      summary: Save MCPs
-      tags:
-      - mcp
   /api/mcp/public/{id}:
     delete:
       description: Delete an MCP by ID
@@ -4713,7 +4688,79 @@ paths:
       summary: Update MCP status
       tags:
       - mcp
-  /api/mcp/public/all:
+  /api/mcp/publics/:
+    get:
+      description: Get a list of MCPs with pagination and filtering
+      parameters:
+      - description: Page number
+        in: query
+        name: page
+        type: integer
+      - description: Items per page
+        in: query
+        name: per_page
+        type: integer
+      - description: hosted or local
+        in: query
+        name: type
+        type: string
+      - description: MCP id
+        in: query
+        name: id
+        type: string
+      - description: Search keyword
+        in: query
+        name: keyword
+        type: string
+      - description: MCP status
+        in: query
+        name: status
+        type: integer
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            allOf:
+            - $ref: '#/definitions/middleware.APIResponse'
+            - properties:
+                data:
+                  items:
+                    $ref: '#/definitions/controller.PublicMCPResponse'
+                  type: array
+              type: object
+      security:
+      - ApiKeyAuth: []
+      summary: Get MCPs
+      tags:
+      - mcp
+    post:
+      consumes:
+      - application/json
+      description: Save a list of MCPs
+      parameters:
+      - description: MCP object
+        in: body
+        name: mcp
+        required: true
+        schema:
+          items:
+            $ref: '#/definitions/model.PublicMCP'
+          type: array
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: OK
+          schema:
+            $ref: '#/definitions/middleware.APIResponse'
+      security:
+      - ApiKeyAuth: []
+      summary: Save MCPs
+      tags:
+      - mcp
+  /api/mcp/publics/all:
     get:
       description: Get all MCPs with filtering
       parameters:

+ 20 - 3
core/relay/plugin/web-search/README.md

@@ -2,7 +2,7 @@
 
 ## Overview
 
-The Web Search Plugin is a plugin that provides real-time web search capabilities for AI models, supporting multiple search engines (Google, Bing, Arxiv), with automatic search query rewriting and search result formatting.
+The Web Search Plugin is a plugin that provides real-time web search capabilities for AI models, supporting multiple search engines (Google, Bing, BingCN, Arxiv, SearchXNG), with automatic search query rewriting and search result formatting.
 
 ## Configuration Example
 
@@ -79,7 +79,7 @@ Each search engine configuration contains the following fields:
 
 | Field | Type | Required | Description |
 |-------|------|----------|-------------|
-| `type` | string | Yes | Search engine type: `google`, `bing`, `arxiv`, `searchxng` |
+| `type` | string | Yes | Search engine type: `google`, `bing`, `bingcn`, `arxiv`, `searchxng` |
 | `max_results` | int | No | Maximum results for this engine |
 | `spec` | object | Depends on type | Engine-specific configuration parameters |
 
@@ -115,6 +115,18 @@ Each search engine configuration contains the following fields:
 |-------|------|----------|-------------|
 | `api_key` | string | Yes | Bing Search API key |
 
+##### BingCN Search Engine Configuration (`spec`)
+
+```json
+
+{
+    "type": "bingcn",
+    "spec": {}
+}
+```
+
+BingCN search engine requires no additional configuration parameters.
+
 ##### Arxiv Search Engine Configuration (`spec`)
 
 ```json
@@ -153,6 +165,7 @@ Users can add the `web_search_options` field in their requests to control search
         }
     ],
     "web_search_options": {
+        "enable": true,
         "search_context_size": "medium"
     }
 }
@@ -162,6 +175,7 @@ Users can add the `web_search_options` field in their requests to control search
 
 | Field | Type | Options | Description |
 |-------|------|---------|-------------|
+| `enable` | bool | - | Whether to enable search, if `false`, search will not be enabled |
 | `search_context_size` | string | `low`, `medium`, `high` | Controls the size of search context, affecting the number and depth of search queries |
 
 #### search_context_size Details
@@ -194,7 +208,9 @@ The Web Search plugin is enabled under the following conditions:
             "content": "What's the weather like today?"
         }
     ],
-    "web_search_options": {}
+    "web_search_options": {
+        "enable": true
+    }
 }
 ```
 
@@ -210,6 +226,7 @@ The Web Search plugin is enabled under the following conditions:
         }
     ],
     "web_search_options": {
+        "enable": true,
         "search_context_size": "high"
     }
 }

+ 20 - 3
core/relay/plugin/web-search/README.zh.md

@@ -2,7 +2,7 @@
 
 ## 概述
 
-Web Search Plugin 是一个为 AI 模型提供实时网络搜索能力的插件,支持多种搜索引擎(Google、Bing、Arxiv),能够自动重写搜索查询并格式化搜索结果。
+Web Search Plugin 是一个为 AI 模型提供实时网络搜索能力的插件,支持多种搜索引擎(Google、Bing、BingCN、Arxiv、SearchXNG),能够自动重写搜索查询并格式化搜索结果。
 
 ## 配置示例
 
@@ -79,7 +79,7 @@ Web Search Plugin 是一个为 AI 模型提供实时网络搜索能力的插件
 
 | 字段 | 类型 | 必填 | 说明 |
 |------|------|------|------|
-| `type` | string | 是 | 搜索引擎类型:`google`、`bing`、`arxiv`、`searchxng` |
+| `type` | string | 是 | 搜索引擎类型:`google`、`bing`、`bingcn`、`arxiv`、`searchxng` |
 | `max_results` | int | 否 | 该引擎的最大结果数量 |
 | `spec` | object | 视类型而定 | 引擎特定的配置参数 |
 
@@ -115,6 +115,18 @@ Web Search Plugin 是一个为 AI 模型提供实时网络搜索能力的插件
 |------|------|------|------|
 | `api_key` | string | 是 | Bing Search API 密钥 |
 
+
+##### BingCN 搜索引擎配置 (`spec`)
+
+```json
+{
+    "type": "bingcn",
+    "spec": {}
+}
+```
+
+BingCN 搜索引擎无需额外配置参数,使用默认配置。
+
 ##### Arxiv 搜索引擎配置 (`spec`)
 
 ```json
@@ -153,6 +165,7 @@ Arxiv 搜索引擎无需额外配置参数。
         }
     ],
     "web_search_options": {
+        "enable": true,
         "search_context_size": "medium"
     }
 }
@@ -162,6 +175,7 @@ Arxiv 搜索引擎无需额外配置参数。
 
 | 字段 | 类型 | 可选值 | 说明 |
 |------|------|--------|------|
+| `enable` | bool | - | 是否启用搜索,如果为 `false`,则不启用搜索 |
 | `search_context_size` | string | `low`、`medium`、`high` | 控制搜索上下文的大小,影响搜索查询的数量和深度 |
 
 #### search_context_size 详解
@@ -194,7 +208,9 @@ Web Search 插件在以下情况下会被启用:
             "content": "今天的天气如何?"
         }
     ],
-    "web_search_options": {}
+    "web_search_options": {
+        "enable": true
+    }
 }
 ```
 
@@ -210,6 +226,7 @@ Web Search 插件在以下情况下会被启用:
         }
     ],
     "web_search_options": {
+        "enable": true,
         "search_context_size": "high"
     }
 }

+ 4 - 0
core/relay/plugin/web-search/search.go

@@ -146,6 +146,10 @@ func (p *WebSearch) ConvertRequest(
 	if !pluginConfig.ForceSearch && !hasWebSearchOptions {
 		return do.ConvertRequest(meta, store, req)
 	}
+	webSearchEnable, ok := webSearchOptions["enable"].(bool)
+	if ok && !webSearchEnable {
+		return do.ConvertRequest(meta, store, req)
+	}
 
 	// Extract user query from messages
 	messages, ok := chatRequest["messages"].([]any)

+ 6 - 3
core/router/api.go

@@ -186,13 +186,16 @@ func SetAPIRouter(router *gin.Engine) {
 			monitorRoute.GET("/models", controller.GetModelsErrorRate)
 			monitorRoute.GET("/banned_channels", controller.GetAllBannedModelChannels)
 		}
+		publicsMcpRoute := apiRouter.Group("/mcp/publics")
+		{
+			publicsMcpRoute.GET("/", mcp.GetPublicMCPs)
+			publicsMcpRoute.GET("/all", mcp.GetAllPublicMCPs)
+			publicsMcpRoute.POST("/", mcp.SavePublicMCPs)
+		}
 		publicMcpRoute := apiRouter.Group("/mcp/public")
 		{
-			publicMcpRoute.GET("/", mcp.GetPublicMCPs)
-			publicMcpRoute.GET("/all", mcp.GetAllPublicMCPs)
 			publicMcpRoute.GET("/:id", mcp.GetPublicMCPByID)
 			publicMcpRoute.POST("/", mcp.CreatePublicMCP)
-			publicMcpRoute.PUT("/", mcp.SavePublicMCPs)
 			publicMcpRoute.POST("/:id", mcp.UpdatePublicMCP)
 			publicMcpRoute.PUT("/:id", mcp.SavePublicMCP)
 			publicMcpRoute.DELETE("/:id", mcp.DeletePublicMCP)

+ 2 - 2
web/src/api/mcp.ts

@@ -93,11 +93,11 @@ export const getMCPs = (params: {
   keyword?: string
   status?: number
 }) => {
-  return get<MCPListResponse>('/mcp/public/', { params })
+  return get<MCPListResponse>('/mcp/publics/', { params })
 }
 
 export const getAllMCPs = (params?: { status?: number }) => {
-  return get<PublicMCP[]>('/mcp/public/all', { params })
+  return get<PublicMCP[]>('/mcp/publics/all', { params })
 }
 
 export const getMCPById = (id: string) => {