mirror of
https://github.com/alibaba/higress.git
synced 2026-06-09 04:37:31 +08:00
feat: implement hgctl agent module (#3267)
This commit is contained in:
@@ -31,7 +31,33 @@ type HigressClient struct {
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
type HimarketClient struct {
|
||||
baseURL string
|
||||
username string
|
||||
password string
|
||||
httpClient *http.Client
|
||||
jwtToken string
|
||||
}
|
||||
|
||||
// type ClientType string
|
||||
|
||||
// const (
|
||||
// HigressClientType ClientType = "higress"
|
||||
// HimarketClientType ClientType = "himarket"
|
||||
// )
|
||||
|
||||
// func NewClient(clientType ClientType, baseURL, username, password string) Client {
|
||||
// switch clientType {
|
||||
// case HimarketClientType:
|
||||
// return NewHimarketClient(baseURL, username, password)
|
||||
// case HigressClientType:
|
||||
// fallthrough
|
||||
// default:
|
||||
// return NewHigressClient(baseURL, username, password)
|
||||
// }
|
||||
// }
|
||||
func NewHigressClient(baseURL, username, password string) *HigressClient {
|
||||
|
||||
client := &HigressClient{
|
||||
baseURL: baseURL,
|
||||
username: username,
|
||||
@@ -44,6 +70,19 @@ func NewHigressClient(baseURL, username, password string) *HigressClient {
|
||||
return client
|
||||
}
|
||||
|
||||
func NewHimarketClient(baseURL, username, password string) *HimarketClient {
|
||||
client := &HimarketClient{
|
||||
baseURL: baseURL,
|
||||
username: username,
|
||||
password: password,
|
||||
httpClient: &http.Client{
|
||||
Timeout: 30 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
func (c *HigressClient) Get(path string) ([]byte, error) {
|
||||
return c.request("GET", path, nil)
|
||||
}
|
||||
@@ -59,6 +98,133 @@ func (c *HigressClient) Put(path string, data interface{}) ([]byte, error) {
|
||||
func (c *HigressClient) Delete(path string) ([]byte, error) {
|
||||
return c.request("DELETE", path, nil)
|
||||
}
|
||||
|
||||
func (c *HimarketClient) getJWTToken() error {
|
||||
loginURL := c.baseURL + "/api/v1/admins/login"
|
||||
|
||||
loginData := map[string]string{
|
||||
"username": c.username,
|
||||
"password": c.password,
|
||||
}
|
||||
|
||||
jsonData, err := json.Marshal(loginData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal login data: %w", err)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", loginURL, bytes.NewBuffer(jsonData))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create login request: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("login request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return fmt.Errorf("login failed with status code: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
var response map[string]interface{}
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read login response: %w", err)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(respBody, &response); err != nil {
|
||||
return fmt.Errorf("failed to parse login response: %w", err)
|
||||
}
|
||||
|
||||
// fmt.Println(string(respBody))
|
||||
|
||||
if data, ok := response["data"].(map[string]interface{}); ok {
|
||||
if token, ok := data["access_token"].(string); ok {
|
||||
c.jwtToken = token
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("token not found in login response: %v", response)
|
||||
}
|
||||
|
||||
func (c *HimarketClient) Get(path string) ([]byte, error) {
|
||||
return c.request("GET", path, nil)
|
||||
}
|
||||
|
||||
func (c *HimarketClient) Post(path string, data interface{}) ([]byte, error) {
|
||||
return c.request("POST", path, data)
|
||||
}
|
||||
|
||||
func (c *HimarketClient) Put(path string, data interface{}) ([]byte, error) {
|
||||
return c.request("PUT", path, data)
|
||||
}
|
||||
|
||||
func (c *HimarketClient) request(method, path string, data interface{}) ([]byte, error) {
|
||||
if c.jwtToken == "" {
|
||||
if err := c.getJWTToken(); err != nil {
|
||||
return nil, fmt.Errorf("failed to get JWT token: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
url := c.baseURL + path
|
||||
|
||||
var body io.Reader
|
||||
if data != nil {
|
||||
jsonData, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal request data: %w", err)
|
||||
}
|
||||
body = bytes.NewBuffer(jsonData)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, method, url, body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Authorization", "Bearer "+c.jwtToken)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == 409 {
|
||||
return nil, fmt.Errorf("resource already exists")
|
||||
}
|
||||
|
||||
if resp.StatusCode == 400 {
|
||||
return nil, fmt.Errorf("invalid resource definition")
|
||||
}
|
||||
|
||||
if resp.StatusCode == 500 {
|
||||
return nil, fmt.Errorf("server internal error")
|
||||
}
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return nil, fmt.Errorf("HTTP error %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read response body: %w", err)
|
||||
}
|
||||
|
||||
return respBody, nil
|
||||
}
|
||||
|
||||
func (c *HigressClient) request(method, path string, data interface{}) ([]byte, error) {
|
||||
url := c.baseURL + path
|
||||
|
||||
@@ -92,6 +258,8 @@ func (c *HigressClient) request(method, path string, data interface{}) ([]byte,
|
||||
return nil, fmt.Errorf("resource already exists")
|
||||
}
|
||||
|
||||
// fmt.Println(resp)
|
||||
|
||||
if resp.StatusCode == 400 {
|
||||
return nil, fmt.Errorf("invalid resource definition")
|
||||
}
|
||||
|
||||
@@ -15,7 +15,11 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/alibaba/higress/hgctl/pkg/agent/common"
|
||||
)
|
||||
|
||||
func HandleAddServiceSource(client *HigressClient, body interface{}) ([]byte, error) {
|
||||
@@ -50,22 +54,30 @@ func HandleAddServiceSource(client *HigressClient, body interface{}) ([]byte, er
|
||||
// add MCP server to higress console, example request body as followed:
|
||||
//
|
||||
// {
|
||||
// "name": "mcp-deepwiki",
|
||||
// "description": "",
|
||||
// "type": "DIRECT_ROUTE", // or OPEN_API
|
||||
// "service": "hgctl-deepwiki.dns:443",
|
||||
// "upstreamPathPrefix": "/mcp",
|
||||
// "name": "test",
|
||||
// "description": "123",
|
||||
// "type": "DIRECT_ROUTE",
|
||||
// "services": [
|
||||
// {
|
||||
// "name": "hgctl-deepwiki.dns",
|
||||
// "name": "hgctl-mcp-deepwiki.dns",
|
||||
// "port": 443,
|
||||
// "version": "1.0",
|
||||
// "weight": 100
|
||||
// }
|
||||
// ]
|
||||
// ],
|
||||
// "consumerAuthInfo": {
|
||||
// "type": "key-auth",
|
||||
// "allowedConsumers": []
|
||||
// },
|
||||
// "domains": [],
|
||||
// "directRouteConfig": {
|
||||
// "path": "/mcp",
|
||||
// "transportType": "streamable"
|
||||
// }
|
||||
// }
|
||||
func HandleAddMCPServer(client *HigressClient, body interface{}) ([]byte, error) {
|
||||
data, ok := body.(map[string]interface{})
|
||||
// fmt.Printf("mcpbody: %v\n", data)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to parse request body")
|
||||
}
|
||||
@@ -76,10 +88,6 @@ func HandleAddMCPServer(client *HigressClient, body interface{}) ([]byte, error)
|
||||
if _, ok := data["type"]; !ok {
|
||||
return nil, fmt.Errorf("missing required field 'type' in body")
|
||||
}
|
||||
if _, ok := data["service"]; !ok {
|
||||
return nil, fmt.Errorf("missing required field 'service' in body")
|
||||
}
|
||||
|
||||
// if _, ok := data["upstreamPathPrefix"]; !ok {
|
||||
// return nil, fmt.Errorf("missing required field 'upstreamPathPrefix' in body")
|
||||
// }
|
||||
@@ -97,6 +105,40 @@ func HandleAddMCPServer(client *HigressClient, body interface{}) ([]byte, error)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// return map[mcp-server-name]{}
|
||||
func GetExistingMCPServers(client *HigressClient) (map[string]string, error) {
|
||||
result := make(map[string]string)
|
||||
data, err := HandleListMCPServers(client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var response map[string]interface{}
|
||||
if err := json.Unmarshal(data, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to get product id from response: %s", err)
|
||||
}
|
||||
|
||||
// fmt.Println(response["data"])
|
||||
|
||||
if list, ok := response["data"].([]interface{}); ok {
|
||||
for _, item := range list {
|
||||
if mcp, ok := item.(map[string]interface{}); ok {
|
||||
if name, ok := mcp["name"].(string); ok {
|
||||
result[name] = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func HandleListMCPServers(client *HigressClient) ([]byte, error) {
|
||||
ts := time.Now().Unix()
|
||||
pageNum := 1
|
||||
pageSize := 100
|
||||
return client.Get(fmt.Sprintf("/v1/mcpServer?ts=%d&pageNum=%d&pageSize=%d", ts, pageNum, pageSize))
|
||||
}
|
||||
|
||||
// add OpenAPI MCP tools to higress console, example request body:
|
||||
//
|
||||
// {
|
||||
@@ -127,3 +169,155 @@ func HandleAddMCPServer(client *HigressClient, body interface{}) ([]byte, error)
|
||||
func HandleAddOpenAPITool(client *HigressClient, body interface{}) ([]byte, error) {
|
||||
return client.Put("/v1/mcpServer", body)
|
||||
}
|
||||
|
||||
func HandleAddAIProviderService(client *HigressClient, body interface{}) ([]byte, error) {
|
||||
return client.Post("/v1/ai/providers", body)
|
||||
|
||||
}
|
||||
|
||||
func HandleAddAIRoute(client *HigressClient, body interface{}) ([]byte, error) {
|
||||
return client.Post("/v1/ai/routes", body)
|
||||
}
|
||||
|
||||
func HandleAddRoute(client *HigressClient, body interface{}) ([]byte, error) {
|
||||
return client.Post("/v1/routes", body)
|
||||
}
|
||||
|
||||
// Himarket-related
|
||||
func HandleAddHigressInstance(client *HimarketClient, body interface{}) ([]byte, error) {
|
||||
// This api will not return the higress-gatway-id
|
||||
return client.Post("/api/v1/gateways", body)
|
||||
}
|
||||
|
||||
func (c *HimarketClient) getProduct(typ common.ProductType) ([]byte, error) {
|
||||
return c.Get(fmt.Sprintf("/api/v1/products?type=%s&page=0&size=30", string(typ)))
|
||||
}
|
||||
|
||||
func (c *HimarketClient) extractGetProductResponse(typ common.ProductType, response map[string]interface{}) map[string]string {
|
||||
result := make(map[string]string)
|
||||
|
||||
data, ok := response["data"].(map[string]interface{})
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
|
||||
content, ok := data["content"].([]interface{})
|
||||
if !ok {
|
||||
return result
|
||||
}
|
||||
|
||||
for _, item := range content {
|
||||
product, ok := item.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
productType, _ := product["type"].(string)
|
||||
if productType != string(typ) {
|
||||
continue
|
||||
}
|
||||
|
||||
name, _ := product["name"].(string)
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
mcpConfig, ok := product["mcpConfig"].(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
serverConfig, ok := mcpConfig["mcpServerConfig"].(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
domains, ok := serverConfig["domains"].([]interface{})
|
||||
if !ok || len(domains) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
path, ok := serverConfig["path"].(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, domainItem := range domains {
|
||||
domainConfig, ok := domainItem.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
domain, _ := domainConfig["domain"].(string)
|
||||
protocol, _ := domainConfig["protocol"].(string)
|
||||
if domain == "" || protocol == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
port, _ := domainConfig["port"].(float64)
|
||||
url := ""
|
||||
if port == 0 || port == 80 {
|
||||
url = fmt.Sprintf("%s://%s%s", protocol, domain, path)
|
||||
} else {
|
||||
url = fmt.Sprintf("%s://%s:%d%s", protocol, domain, int(port), path)
|
||||
}
|
||||
|
||||
result[name] = url
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (c *HimarketClient) GetDevModelProduct() (map[string]string, error) {
|
||||
data, err := c.getProduct(common.MODEL_API)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed request himarket: %s", err)
|
||||
}
|
||||
var response map[string]interface{}
|
||||
if err := json.Unmarshal(data, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to get model api from response %s", err)
|
||||
}
|
||||
|
||||
return c.extractGetProductResponse(common.MODEL_API, response), nil
|
||||
}
|
||||
|
||||
func (c *HimarketClient) GetDevMCPServerProduct() (map[string]string, error) {
|
||||
data, err := c.getProduct(common.MCP_SERVER)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed request himarket: %s", err)
|
||||
}
|
||||
var response map[string]interface{}
|
||||
if err := json.Unmarshal(data, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to get MCP server from response %s", err)
|
||||
}
|
||||
|
||||
return c.extractGetProductResponse(common.MCP_SERVER, response), nil
|
||||
}
|
||||
|
||||
func HandleListHimarketMCPServers(client *HimarketClient) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func HandleAddAPIProduct(client *HimarketClient, body interface{}) ([]byte, error) {
|
||||
data, err := client.Post("/api/v1/products", body)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
var response map[string]interface{}
|
||||
if err := json.Unmarshal(data, &response); err != nil {
|
||||
return nil, fmt.Errorf("failed to get product id from response: %s", err)
|
||||
}
|
||||
|
||||
if res, ok := response["data"].(map[string]interface{}); ok {
|
||||
if productId, ok := res["productId"].(string); ok {
|
||||
return []byte(productId), nil
|
||||
}
|
||||
}
|
||||
return data, fmt.Errorf("failed to get product id from response")
|
||||
}
|
||||
|
||||
func HandleRefAPIProduct(client *HimarketClient, product_id string, body interface{}) ([]byte, error) {
|
||||
return client.Post(fmt.Sprintf("/api/v1/products/%s/ref", product_id), body)
|
||||
}
|
||||
|
||||
178
hgctl/pkg/agent/services/utils.go
Normal file
178
hgctl/pkg/agent/services/utils.go
Normal file
@@ -0,0 +1,178 @@
|
||||
// Copyright (c) 2025 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package services
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func BuildAIProviderServiceBody(name, url string) map[string]interface{} {
|
||||
customBaseURL := fmt.Sprintf("%s/compatible-mode/v1", url)
|
||||
return map[string]interface{}{
|
||||
"type": "openai",
|
||||
"name": name,
|
||||
"tokens": []string{},
|
||||
"version": 0,
|
||||
"protocol": "openai/v1",
|
||||
"tokenFailoverConfig": map[string]interface{}{
|
||||
"enabled": false,
|
||||
},
|
||||
"proxyName": "",
|
||||
"rawConfigs": map[string]interface{}{
|
||||
"openaiExtraCustomUrls": []string{},
|
||||
"openaiCustomUrl": customBaseURL,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func BuildAddAIRouteBody(name, _url string) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"name": fmt.Sprintf("%s-route", name),
|
||||
// "version": "627198", // It's unecessary to provide when create a new one
|
||||
"domains": []interface{}{},
|
||||
"pathPredicate": map[string]interface{}{
|
||||
"matchType": "PRE",
|
||||
// FIXME: Currently, to use model API in higress user hould follow this pattern:
|
||||
// http://<higress-gateway-ip>/<PRE_MATCH_VALUE>/v1/chat/completions or /v1/embedding
|
||||
// However in Himarket, when connecting the higress ai route as model API, himarket will directly use http://<higress-gateway-ip>/<PRE_MATCH_VALUE>
|
||||
// as the final request url, which will not get to right path. So here we make the matchValue hard-coded as `/v1/chat/completions`
|
||||
"matchValue": "/v1/chat/completions",
|
||||
"caseSensitive": false,
|
||||
"ignoreCase": []string{}, // "ignoreCase": ["ignore"]
|
||||
},
|
||||
"headerPredicates": []interface{}{},
|
||||
"urlParamPredicates": []interface{}{},
|
||||
"upstreams": []interface{}{
|
||||
map[string]interface{}{
|
||||
"provider": name,
|
||||
"weight": 100,
|
||||
"modelMapping": map[string]interface{}{},
|
||||
},
|
||||
},
|
||||
"modelPredicates": []interface{}{},
|
||||
"authConfig": map[string]interface{}{
|
||||
"enabled": false,
|
||||
"allowedCredentialTypes": []interface{}{},
|
||||
"allowedConsumers": []interface{}{},
|
||||
},
|
||||
"fallbackConfig": map[string]interface{}{
|
||||
"enabled": false,
|
||||
"upstreams": nil,
|
||||
"fallbackStrategy": nil,
|
||||
"responseCodes": nil,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func BuildServiceBodyAndSrv(name, urlStr string) (map[string]interface{}, string, string, error) {
|
||||
res, err := url.Parse(urlStr)
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
}
|
||||
|
||||
// add service source
|
||||
srvType := ""
|
||||
srvPort := ""
|
||||
|
||||
if ip := net.ParseIP(res.Hostname()); ip == nil {
|
||||
srvType = "dns"
|
||||
} else {
|
||||
srvType = "static"
|
||||
}
|
||||
|
||||
if res.Port() == "" && res.Scheme == "http" {
|
||||
srvPort = "80"
|
||||
} else if res.Port() == "" && res.Scheme == "https" {
|
||||
srvPort = "443"
|
||||
} else {
|
||||
srvPort = res.Port()
|
||||
}
|
||||
|
||||
// e.g. hgctl-mcp-deepwiki.dns
|
||||
targetSrvName := fmt.Sprintf("%s.%s", name, srvType)
|
||||
|
||||
return map[string]interface{}{
|
||||
"domain": res.Host,
|
||||
"type": srvType,
|
||||
"port": srvPort,
|
||||
"name": name,
|
||||
"proxyName": "",
|
||||
"domainForEdit": res.Host,
|
||||
"protocol": res.Scheme,
|
||||
}, targetSrvName, srvPort, nil
|
||||
}
|
||||
|
||||
func BuildAPIRouteBody(name, srv string) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"name": fmt.Sprintf("%s-route", name),
|
||||
"path": map[string]interface{}{
|
||||
"matchType": "PRE", // default is PREFIX
|
||||
"matchValue": "/process", // default is "/process"
|
||||
"caseSensitive": true,
|
||||
},
|
||||
"authConfig": map[string]interface{}{
|
||||
"enabled": false,
|
||||
},
|
||||
"services": []map[string]interface{}{
|
||||
{
|
||||
"name": srv,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func BuildAddHigressInstanceBody(name, addr, username, password string) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"gatewayName": name,
|
||||
"gatewayType": "HIGRESS",
|
||||
"higressConfig": map[string]interface{}{
|
||||
"address": addr,
|
||||
"username": username,
|
||||
"password": password,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func BuildAPIProductBody(name, desc, typ string) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"name": name, "description": desc, "type": typ,
|
||||
}
|
||||
}
|
||||
|
||||
func BuildRefModelAPIProductBody(gateway_id, product_id, target_route string) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"gatewayId": gateway_id,
|
||||
"sourceType": "GATEWAY",
|
||||
"productId": product_id,
|
||||
"higressRefConfig": map[string]interface{}{
|
||||
"modelRouteName": target_route,
|
||||
"fromGatewayType": "HIGRESS",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func BuildRefMCPAPIProductBody(gateway_id, product_id, mcp_name string) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"gatewayId": gateway_id,
|
||||
"sourceType": "GATEWAY",
|
||||
"productId": product_id,
|
||||
"higressRefConfig": map[string]interface{}{
|
||||
"mcpServerName": mcp_name,
|
||||
"fromGatewayType": "HIGRESS",
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user